Skip to content

Commit

Permalink
Simple report button
Browse files Browse the repository at this point in the history
  • Loading branch information
cherriae committed Feb 2, 2025
1 parent 76c8599 commit 04cc240
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 28 deletions.
39 changes: 39 additions & 0 deletions app/scout/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -931,3 +931,42 @@ def pit_scouting_delete(team_number):
else:
flash("Error deleting pit scouting data", "error")
return redirect(url_for("scouting.pit_scouting"))

@scouting_bp.route("/submit_report", methods=["POST"])
@login_required
def submit_report():
try:
data = {
"data_id": request.form.get("data_id"),
"report_type": request.form.get("report_type"),
"reason": request.form.get("reason"),
"details": request.form.get("details"),
"reporter_id": current_user.get_id()
}

if not all([data["data_id"], data["report_type"],
data["reason"], data["details"]]):
return jsonify({
"success": False,
"message": "All fields are required"
}), 400

success, message = scouting_manager.add_report(data)

if success:
return jsonify({
"success": True,
"message": "Report submitted successfully"
})
else:
return jsonify({
"success": False,
"message": message
}), 500

except Exception as e:
current_app.logger.error(f"Error submitting report: {str(e)}")
return jsonify({
"success": False,
"message": "An error occurred while submitting the report"
}), 500
80 changes: 70 additions & 10 deletions app/scout/scouting_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ def __init__(self, mongo_uri):
def _ensure_collections(self):
"""Ensure required collections exist"""
if "team_data" not in self.db.list_collection_names():
self.db.create_collection("team_data")
# Create indexes
self.db.team_data.create_index([("team_number", 1)])
self.db.team_data.create_index([("scouter_id", 1)])
logger.info("Created team_data collection and indexes")
self.create_collections()

def connect(self):
"""Establish connection to MongoDB with basic error handling"""
Expand All @@ -38,15 +34,18 @@ def connect(self):

# Ensure team_data collection exists
if "team_data" not in self.db.list_collection_names():
self.db.create_collection("team_data")
# Create indexes
self.db.team_data.create_index([("team_number", 1)])
self.db.team_data.create_index([("scouter_id", 1)])
logger.info("Created team_data collection and indexes")
self._extracted_from_connect_4()
except Exception as e:
logger.error(f"Failed to connect to MongoDB: {str(e)}")
raise

def create_collections(self):
self.db.create_collection("team_data")
self.db.team_data.create_index([("team_number", 1)])
self.db.team_data.create_index([("scouter_id", 1)])
logger.info("Created team_data collection and indexes")


def ensure_connected(self):
"""Ensure we have a valid connection, reconnect if necessary"""
try:
Expand Down Expand Up @@ -671,3 +670,64 @@ def delete_pit_scouting(self, team_number, scouter_id):
except Exception as e:
logger.error(f"Error deleting pit scouting data: {str(e)}")
return False

@with_mongodb_retry(retries=3, delay=2)
def add_report(self, data):
"""Add a new report"""
self.ensure_connected()
try:
report_data = {
"data_id": ObjectId(data["data_id"]),
"report_type": data["report_type"],
"reason": data["reason"],
"details": data["details"],
"reporter_id": ObjectId(data["reporter_id"]),
"status": "pending",
"created_at": datetime.now(timezone.utc)
}

result = self.db.reports.insert_one(report_data)
return True, str(result.inserted_id)
except Exception as e:
logger.error(f"Error adding report: {str(e)}")
return False, "An error occurred while submitting the report."

@with_mongodb_retry(retries=3, delay=2)
def get_reports(self, status=None):
"""Get all reports with optional status filter"""
self.ensure_connected()
try:
query = {}
if status:
query["status"] = status

pipeline = [
{"$match": query},
{
"$lookup": {
"from": "users",
"localField": "reporter_id",
"foreignField": "_id",
"as": "reporter"
}
},
{"$unwind": "$reporter"},
{
"$project": {
"_id": 1,
"data_id": 1,
"report_type": 1,
"reason": 1,
"details": 1,
"status": 1,
"created_at": 1,
"reporter_name": "$reporter.username",
"reporter_team": "$reporter.teamNumber"
}
}
]

return list(self.db.reports.aggregate(pipeline))
except Exception as e:
logger.error(f"Error fetching reports: {str(e)}")
return []
8 changes: 6 additions & 2 deletions app/static/js/scout.edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ function resizeCanvas() {
}

function drawBackground() {
if (!bgImage || !bgImage.complete) return;
if (!bgImage || !bgImage.complete) {
return;
}

const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
Expand Down Expand Up @@ -138,7 +140,9 @@ function startDrawing(e) {

function draw(e) {
e.preventDefault();
if (!isDrawing) return;
if (!isDrawing) {
return;
}

const pos = getPointerPosition(e);

Expand Down
2 changes: 1 addition & 1 deletion app/templates/community-guidlines.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Castle is committed to making FRC scouting accessible to every team, regardless

## Expected Behavior
1. **Data Integrity**
- Submiting data that isn't disrespected to the team or their robot.
- Submitting data that isn't disrespected to the team or their robot.
- Double-check your entries before submission
- Report any errors or inconsistencies you notice

Expand Down
23 changes: 13 additions & 10 deletions app/templates/privacy.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,6 @@ Castle is committed to protecting your privacy and securing your data. This poli
- End-to-end encryption for sensitive data
- Regular security audits and updates
- Restricted access controls
- Automated threat detection
- Regular data backups

### 2.3 Data Sharing Controls
- Team administrators control sharing settings
- Explicit consent required for data sharing
- Granular permission settings
- Clear sharing status indicators

## 3. Your Data Rights
### 3.1 Access and Control
Expand All @@ -61,7 +53,7 @@ Castle is committed to protecting your privacy and securing your data. This poli

### 4.2 Reporting Security Concerns
- In-app reporting system
- Direct email: no-reply@castle-scouting.com
- Direct email: (email here)
- 24-hour response commitment
- Regular security status updates

Expand Down Expand Up @@ -100,4 +92,15 @@ We adhere to:
- Industry security standards
- Privacy best practices

Last Updated: [Current Date]
## 9. User Consent
### 9.1 Obtaining Consent
- Explanation of how consent is obtained
- Methods for users to give consent
- Record-keeping of consent

### 9.2 Withdrawing Consent
- Process for withdrawing consent
- Impact of withdrawing consent on services
- Contact information for consent withdrawal

Last Updated: [Current Date]
83 changes: 82 additions & 1 deletion app/templates/scouting/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,49 @@ <h4 class="text-md font-medium text-gray-700 mb-2">Auto Notes</h4>
</div>
</div>

<!-- Report Modal -->
<div id="reportModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 flex items-center justify-center">
<div class="relative bg-white rounded-lg shadow-xl max-w-md w-full m-4">
<div class="p-6">
<h3 class="text-lg font-medium text-gray-900 mb-4">Report Content</h3>
<form id="reportForm" method="POST">
<input type="hidden" id="reportDataId" name="data_id">
<input type="hidden" id="reportType" name="report_type">

<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">Reason for Report</label>
<select id="reportReason" name="reason" class="w-full rounded-md border border-gray-300 p-2" required>
<option value="">Select a reason...</option>
<option value="inappropriate">Inappropriate Content</option>
<option value="inaccurate">Inaccurate Data</option>
<option value="spam">Spam</option>
<option value="other">Other</option>
</select>
</div>

<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">Additional Details</label>
<textarea id="reportDetails" name="details" rows="3"
class="w-full rounded-md border border-gray-300 p-2"
placeholder="Please provide additional details about your report..."
required></textarea>
</div>

<div class="flex justify-end gap-3">
<button type="button" onclick="closeReportModal()"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200">
Cancel
</button>
<button type="submit"
class="px-4 py-2 text-sm font-medium text-white bg-red-600 rounded-md hover:bg-red-700">
Submit Report
</button>
</div>
</form>
</div>
</div>
</div>

<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-8 gap-4">
<div class="flex flex-col gap-4">
<h1 class="text-2xl font-bold">Team Data</h1>
Expand Down Expand Up @@ -249,7 +292,11 @@ <h2 class="text-xl font-semibold mb-4 bg-gray-100 rounded px-4 py-2">
<span class="sm:hidden">🗑️</span>
</a>
{% else %}
<span class="text-gray-400 text-sm">No Access</span>
<button onclick="openReportModal('{{ data._id }}', 'match')"
class="text-red-600 hover:text-red-900">
<span class="hidden sm:inline">Report</span>
<span class="sm:hidden">⚠️</span>
</button>
{% endif %}
</div>
</td>
Expand All @@ -264,4 +311,38 @@ <h2 class="text-xl font-semibold mb-4 bg-gray-100 rounded px-4 py-2">
</div>

<script src="{{ url_for('static', filename='js/scout.list.js') }}"></script>
<script>
function openReportModal(dataId, type) {
document.getElementById('reportDataId').value = dataId;
document.getElementById('reportType').value = type;
document.getElementById('reportModal').classList.remove('hidden');
}

function closeReportModal() {
document.getElementById('reportModal').classList.add('hidden');
document.getElementById('reportForm').reset();
}

document.getElementById('reportForm').addEventListener('submit', async function(e) {
e.preventDefault();

const formData = new FormData(this);
try {
const response = await fetch("{{ url_for('scouting.submit_report') }}", {
method: 'POST',
body: formData
});

const result = await response.json();
if (result.success) {
alert('Report submitted successfully');
closeReportModal();
} else {
alert(result.message || 'Error submitting report');
}
} catch (error) {
alert('Error submitting report');
}
});
</script>
{% endblock %}
Loading

0 comments on commit 04cc240

Please sign in to comment.