Spaces:
Sleeping
Sleeping
Multi-select areas, colour-coded area tags, Creighton students only, titles removed, Creighton blue
4f5fdec verified Download app/fields.py from stevafernandes/CURAS: direct link, hf CLI and curl.
- Browser
- Download file 9.54 kB
-
https://huggingface.co/spaces/stevafernandes/CURAS/resolve/main/app/fields.py
- Command line
-
hf download hf://spaces/stevafernandes/CURAS/app/fields.py
-
curl -L -o fields.py https://huggingface.co/spaces/stevafernandes/CURAS/resolve/main/app/fields.py
9.54 kB
| """Field definitions shared by the public pages, the submission form, the admin | |
| editor and the Excel importer. | |
| Every content field of a listing is described once here so labels, help text, | |
| limits and vocabularies cannot drift apart between the form and the database. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Optional | |
| # Closed vocabularies. The first spelling is canonical; the rest are accepted | |
| # variants (case-insensitive) that are normalised to the canonical spelling. | |
| OPPORTUNITY_TYPES = { | |
| "Credit": ["credit", "academic credit"], | |
| "Volunteer": ["volunteer"], | |
| "Paid": ["paid"], | |
| "Federal Work Study": ["federal work study", "federal workstudy", "federal work-study"], | |
| } | |
| TERMS = { | |
| "Fall": ["fall"], | |
| "Spring": ["spring"], | |
| "Summer": ["summer"], | |
| } | |
| STANDINGS = { | |
| "Freshman": ["freshman", "freshmen"], | |
| "Sophomore": ["sophomore", "sophmore"], | |
| "Junior": ["junior"], | |
| "Senior": ["senior"], | |
| } | |
| # Areas of study that appear in the September 2026 CURAS workbook, with the | |
| # misspellings found there mapped to the canonical spelling. | |
| AREAS_OF_STUDY = { | |
| "All majors": ["all majors", "all majors welcome"], | |
| "American Studies": ["american studies"], | |
| "Art History": ["art history"], | |
| "Biochemistry": ["biochemistry", "biochemisty"], | |
| "Biology": ["biology"], | |
| "Biomedical Physics": ["biomedical physics"], | |
| "Business": ["business"], | |
| "Chemistry": ["chemistry"], | |
| "Classical and Near Eastern Civilizations": ["classical and near eastern civilizations"], | |
| "Classical Languages": ["classical languages"], | |
| "Communication Studies": ["communication studies"], | |
| "Computer Science": ["computer science"], | |
| "Cultural Anthropology": ["cultural anthropology"], | |
| "Data Science": ["data science"], | |
| "Economics": ["economics"], | |
| "Education": ["education"], | |
| "Environmental Science": ["environmental science", "enviromental science", "envirometnal science"], | |
| "Exercise Science": ["exercise science", "exerscise science"], | |
| "Fine and Performing Arts": ["fine and performing arts"], | |
| "Foreign Languages": ["foreign languages"], | |
| "French": ["french"], | |
| "Gender Studies": ["gender studies"], | |
| "German": ["german"], | |
| "Health Administration and Policy": [ | |
| "health administration and policy", | |
| "health administration policy", | |
| "health administrationand policy", | |
| ], | |
| "History": ["history"], | |
| "International Business": ["international business"], | |
| "International Relations": ["international relations"], | |
| "Mathematics": ["mathematics"], | |
| "Medical Anthropology": ["medical anthropology", "medical anthrolpology"], | |
| "Neuroscience": ["neuroscience", "neursocience"], | |
| "Nursing": ["nursing"], | |
| "Philosophy": ["philosophy"], | |
| "Physics": ["physics"], | |
| "Political Science": ["political science"], | |
| "Pre-Health Professions": ["pre-health professions", "pre health professions"], | |
| "Psychology": ["psychology"], | |
| "Social Work": ["social work"], | |
| "Sociology": ["sociology"], | |
| "Spanish and Hispanic Studies": ["spanish and hispanic studies"], | |
| "Sustainability": ["sustainability"], | |
| "Theology": ["theology"], | |
| } | |
| # Lengths are generous upper bounds to stop abuse, not editorial limits. | |
| SHORT = 300 | |
| MEDIUM = 2000 | |
| LONG = 8000 | |
| class Field: | |
| key: str | |
| label: str | |
| kind: str # "text" | "textarea" | "url" | "choices" | |
| required: bool = False | |
| help: str = "" | |
| maxlen: int = MEDIUM | |
| choices: Optional[dict] = None # for kind == "choices": canonical -> variants | |
| allow_other: bool = True # for kind == "choices": free-text addition | |
| placeholder: str = "" | |
| # Order here is the order on the submission form, the admin editor and the | |
| # public "full details" view. | |
| CONTENT_FIELDS: list[Field] = [ | |
| Field("faculty_name", "Faculty name", "text", required=True, maxlen=SHORT, | |
| help="As it should appear publicly, for example Dr. Jane Smith."), | |
| Field("faculty_profile_url", "Faculty profile link", "url", maxlen=SHORT, | |
| help="Creighton campus directory page or another faculty profile page (https://...)."), | |
| Field("researchworks_url", "ResearchWorks profile link", "url", maxlen=SHORT, | |
| help="Link to your ResearchWorks profile and publications, or a lab website."), | |
| Field("keywords", "Keywords", "text", maxlen=SHORT, | |
| help="Separate keywords with commas."), | |
| Field("description", "Description", "textarea", required=True, maxlen=LONG, | |
| help="What the research is about and what students would do."), | |
| Field("methods", "Methods and techniques", "textarea", maxlen=LONG), | |
| Field("opportunity_type", "Type of opportunity", "choices", required=True, | |
| choices=OPPORTUNITY_TYPES, maxlen=SHORT), | |
| Field("term_availability", "Term availability", "choices", required=True, | |
| choices=TERMS, maxlen=SHORT), | |
| Field("commitment", "Commitment", "textarea", maxlen=MEDIUM, | |
| help="Expected hours per week and duration, for example 5-10 hours per week for two semesters."), | |
| Field("standing", "Standing (year)", "choices", required=True, | |
| choices=STANDINGS, maxlen=SHORT), | |
| Field("prerequisites", "Prerequisites", "textarea", maxlen=MEDIUM, | |
| help="Courses, skills or experience students should have. Write None if there are none."), | |
| Field("areas_of_study", "Areas of study", "choices", required=True, | |
| choices=AREAS_OF_STUDY, maxlen=MEDIUM, | |
| help="Majors or fields the opportunity suits."), | |
| Field("mentorship", "Mentorship", "textarea", maxlen=MEDIUM, | |
| help="How you work with students, for example meeting frequency and supervision style."), | |
| Field("previous_student_work", "Previous student work", "textarea", maxlen=LONG, | |
| help="Examples of past student projects, presentations or publications."), | |
| Field("faculty_note", "Faculty note", "textarea", maxlen=MEDIUM, | |
| help="Anything else students should know."), | |
| Field("application_deadline", "Application deadline", "text", required=True, maxlen=SHORT, | |
| help="Rolling, a date, or a note about current availability.", placeholder="Rolling"), | |
| Field("application_requirements", "Application requirements", "textarea", required=True, | |
| maxlen=MEDIUM, help="What students should send to express interest, for example an introductory email."), | |
| ] | |
| FIELD_KEYS = [f.key for f in CONTENT_FIELDS] | |
| FIELDS_BY_KEY = {f.key: f for f in CONTENT_FIELDS} | |
| # Fields shown in the collapsed summary on the public page; the rest are in | |
| # the expandable "Full details" section. | |
| SUMMARY_KEYS = ["opportunity_type", "term_availability", "standing", "areas_of_study", "application_deadline"] | |
| # Submitter fields (not part of the listing itself). | |
| SUBMITTER_FIELDS: list[Field] = [ | |
| Field("submitter_name", "Your name", "text", required=True, maxlen=SHORT), | |
| Field("submitter_netid", "Your NetID", "text", required=True, maxlen=64, | |
| help="Your Creighton NetID. Letters and digits only."), | |
| Field("submitter_email", "Your email", "text", maxlen=SHORT, | |
| help="Optional. Used only if CURAS has a question about your submission."), | |
| ] | |
| LIST_SEPARATOR = ", " | |
| # Shown on every listing: who may apply. | |
| ELIGIBILITY_NOTE = "Creighton students only" | |
| # Colour code for areas of study, shown as tags on the public page. Each key is | |
| # a CSS class suffix defined in static/style.css (pastel background, dark text). | |
| # Areas that often appear together on one listing are given different colours | |
| # where possible; related fields share a hue. | |
| AREA_COLORS = { | |
| "All majors": "all", | |
| "American Studies": "brown", | |
| "Art History": "pink", | |
| "Biochemistry": "red", | |
| "Biology": "green", | |
| "Biomedical Physics": "sky", | |
| "Business": "gold", | |
| "Chemistry": "teal", | |
| "Classical and Near Eastern Civilizations": "brown", | |
| "Classical Languages": "sky", | |
| "Communication Studies": "orange", | |
| "Computer Science": "indigo", | |
| "Cultural Anthropology": "teal", | |
| "Data Science": "lime", | |
| "Economics": "gold", | |
| "Education": "mint", | |
| "Environmental Science": "lime", | |
| "Exercise Science": "orange", | |
| "Fine and Performing Arts": "magenta", | |
| "Foreign Languages": "purple", | |
| "French": "purple", | |
| "Gender Studies": "magenta", | |
| "German": "purple", | |
| "Health Administration and Policy": "yellow", | |
| "History": "brown", | |
| "International Business": "sky", | |
| "International Relations": "teal", | |
| "Mathematics": "purple", | |
| "Medical Anthropology": "brown", | |
| "Neuroscience": "yellow", | |
| "Nursing": "magenta", | |
| "Philosophy": "magenta", | |
| "Physics": "blue", | |
| "Political Science": "red", | |
| "Pre-Health Professions": "mint", | |
| "Psychology": "pink", | |
| "Social Work": "gold", | |
| "Sociology": "brown", | |
| "Spanish and Hispanic Studies": "purple", | |
| "Sustainability": "mint", | |
| "Theology": "magenta", | |
| } | |
| OTHER_AREA_COLOR = "gray" | |
| def area_color(item: Optional[str]) -> str: | |
| """CSS colour key for one area-of-study item (free text gets the neutral key).""" | |
| if not item: | |
| return OTHER_AREA_COLOR | |
| wanted = item.strip().lower() | |
| for name, key in AREA_COLORS.items(): | |
| if name.lower() == wanted: | |
| return key | |
| return OTHER_AREA_COLOR | |
| def split_list(value: Optional[str]) -> list[str]: | |
| """Split a stored comma-separated list field into items.""" | |
| if not value: | |
| return [] | |
| return [p.strip() for p in value.split(",") if p.strip()] | |