diff --git a/.gitignore b/.gitignore index 0932424..5c3be03 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ *.pyc -madras/local_settings.py +__pycache__/ + db.sqlite3 +db.sqlite3-journal + +venv/ +env/ diff --git a/README.md b/README.md index aa540a5..f2ad5cc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,13 @@ # Madras Madras is the backend for a cloud-based hackathon management system that aims to bring together the many different hackathon organizing systems into one centralized service to minimize the need to migrate data between hacker applications, registration, check-in, announcements, reimbursement, judging, and prizes. Created with the goal of making organizing hackathons easier for everyone. + +## Setup + +```bash +virtualenv --python=python3 venv +source venv/bin/activate +pip install -r requirements.txt +./manage.py migrate +./manage.py runserver +``` diff --git a/apps/director/models.py b/apps/director/models.py index f3da99f..345e703 100644 --- a/apps/director/models.py +++ b/apps/director/models.py @@ -10,7 +10,7 @@ def __str__(self): class Hackathon(models.Model): name = models.CharField(max_length=128) - organization = models.ForeignKey(Organization, related_name="hackathons") + organization = models.ForeignKey(Organization, related_name="hackathons", on_delete=models.CASCADE) def __str__(self): return self.name diff --git a/apps/reader/models.py b/apps/reader/models.py index 8a71019..6bde2b7 100644 --- a/apps/reader/models.py +++ b/apps/reader/models.py @@ -6,13 +6,13 @@ class Reader(models.Model): - user = models.OneToOneField(User, related_name="reader") - organization = models.ForeignKey(Organization, related_name="readers") + user = models.OneToOneField(User, related_name="reader", on_delete=models.CASCADE) + organization = models.ForeignKey(Organization, related_name="readers", on_delete=models.CASCADE) hackathons = models.ManyToManyField(Hackathon, related_name="readers") class Rating(models.Model): - application = models.OneToOneField(Application, related_name="rating") + application = models.OneToOneField(Application, related_name="rating", on_delete=models.CASCADE) class RatingField(models.Model): @@ -24,7 +24,7 @@ class RatingField(models.Model): (TYPE_MULTIPLE_CHOICE, "Multiple choice"), ) - rating = models.ForeignKey(Rating, related_name="fields") + rating = models.ForeignKey(Rating, related_name="fields", on_delete=models.CASCADE) type = models.CharField(max_length=16, choices=TYPE_CHOICES) prompt = models.CharField(max_length=64) min_number = models.IntegerField(default=-1) @@ -33,6 +33,6 @@ class RatingField(models.Model): class RatingResponse(models.Model): - reader = models.ForeignKey(Reader, related_name="ratings") - applicant = models.ForeignKey(Applicant, related_name="ratings") + reader = models.ForeignKey(Reader, related_name="ratings", on_delete=models.CASCADE) + applicant = models.ForeignKey(Applicant, related_name="ratings", on_delete=models.CASCADE) data = models.TextField() diff --git a/apps/reader/utils.py b/apps/reader/utils.py index 085c166..bdec968 100644 --- a/apps/reader/utils.py +++ b/apps/reader/utils.py @@ -1,7 +1,9 @@ import requests +import geopy.distance from github3 import login +#helper function that gets the number of contributions in the last year def get_contributions(github_user_name): url = "https://github.com/" + github_user_name resp = requests.get(url) @@ -11,7 +13,9 @@ def get_contributions(github_user_name): index_of_contribution + 37: index_of_contribution + 45] return [int(s) for s in substring_of_contribution.split() if s.isdigit()][0] - +#gets the number of followers, number of repositories, +#number of contributions in past year and number of +#self starred repositories def get_metrics_github(github_user_name): gh = login("GITHUB_USER_NAME", password="GITHUB_PASSWORD") user = gh.user(github_user_name) @@ -32,3 +36,63 @@ def get_metrics_github(github_user_name): "num_contributions": get_contributions(github_user_name), "self_star_repos": len(star_repos), } + + +# +#Travel reimburstment +# + +def address_to_lat_long(address): + location = geolocator.geocode(address) + return { + "lat": location.latitude, + "long": location.longitude + } + +KM_TO_MILES = 0.621371 + +#Source: http://www.tps.ucsb.edu/commuter-cost-calculator +AVERAGE_TRANSIT_COST_PER_MILE = .608 + +#Source: https://goo.gl/rDjXe3 +#Fare = $30 + (Distance * $0.08) +AVERAGE_AIRLINE_FARE = 30 +AVERAGE_AIRLINE_COST_PER_MILE = .08 + +#because numbers are from 2015 +INFLATION_FACTOR = 1.03 + +#constants to define +#DISTANCE_TO_NEAREST_AIRPORT is the distance to the nearest airport from +#from the venue in miles +VENUE_LAT = 0 +VENUE_LONG = 0 +NEAREST_AIRPORT_LAT = 0 +NEAREST_AIRPORT_LONG = 0 +DISTANCE_TO_NEAREST_AIRPORT = 0 + +#estimates the cost of travel from lat/long coordinates of user +#all lat and long are doubles + +def calculate_travel_est(lat1, long1): + origin = (lat1, long1) + nearest_airport = (NEAREST_AIRPORT_LAT, NEAREST_AIRPORT_LONG) + venue = (VENUE_LAT, VENUE_LONG) + #multiply by 2 to travel both ways + origin_to_venue = (geopy.distance.vincenty(origin, venue).km)* \ + KM_TO_MILES * 2 + origin_to_airport = (geopy.distance.vincenty(origin, nearest_airport).km)* \ + KM_TO_MILES * 2 + just_transit_costs = .608 * origin_to_venue * INFLATION_FACTOR + airline_costs = origin_to_airport * AVERAGE_AIRLINE_COST_PER_MILE + \ + AVERAGE_AIRLINE_FARE + airport_to_uni_costs = DISTANCE_TO_NEAREST_AIRPORT * \ + AVERAGE_TRANSIT_COST_PER_MILE * 2 + total_airline_costs = (airline_costs + airport_to_uni_costs) * \ + INFLATION_FACTOR + #now compare prices + if (just_transit_costs >= total_airline_costs): + return(total_airline_costs) + else: + return(just_transit_costs) + diff --git a/apps/reader/views.py b/apps/reader/views.py index 9e5f8ca..532eaa7 100644 --- a/apps/reader/views.py +++ b/apps/reader/views.py @@ -12,7 +12,8 @@ from apps.reader import serializers from apps.reader.models import Applicant, RatingResponse from apps.reader.utils import get_metrics_github - +from apps.reader.utils import calculate_travel_est +from apps.reader.utils import address_to_lat_long class Rating(APIView): permission_classes = (IsAuthenticated,) @@ -42,6 +43,7 @@ def get(self, request): rand_pk = random.randint(0, Applicant.objects.all().count() - 1) rand_app = Applicant.objects.get(pk=rand_pk) github_array = get_metrics_github(rand_app.github_user_name) + user_addresss_coords = address_to_lat_long(rand_app.address) return Response( { "applicant_id": rand_app.pk, @@ -51,7 +53,10 @@ def get(self, request): "num_followers": github_array["NumFollowers"], "num_repos": github_array["NumRepos"], "num_contributions": github_array["NumContributions"], - "self_star_repos": github_array["selfStarRepos"] + "self_star_repos": github_array["selfStarRepos"], + "travel_reimbursement" : \ + calculate_travel_est(user_addresss_coords["lat"], \ + user_addresss_coords["long"]) }, status=status.HTTP_200_OK, ) diff --git a/apps/registration/models.py b/apps/registration/models.py index c98bcac..06eb6bf 100644 --- a/apps/registration/models.py +++ b/apps/registration/models.py @@ -17,7 +17,7 @@ class Application(models.Model): name = models.CharField(max_length=128) status = models.CharField(max_length=16, choices=STATUS_CHOICES) - hackathon = models.ForeignKey(Hackathon, related_name="applications") + hackathon = models.ForeignKey(Hackathon, related_name="applications", on_delete=models.CASCADE) def __str__(self): return "{} ({})".format(self.name, self.hackathon) @@ -36,7 +36,7 @@ class ApplicationField(models.Model): (TYPE_LONG_ANSWER, "Long Answer"), ) - application = models.ForeignKey(Application, related_name="fields") + application = models.ForeignKey(Application, related_name="fields", on_delete=models.CASCADE) ordering = models.IntegerField() type = models.CharField(max_length=32, choices=TYPE_CHOICES) prompt = models.CharField(max_length=256) @@ -48,7 +48,7 @@ def __str__(self): class ApplicantTeam(models.Model): - hackathon = models.ForeignKey(Hackathon) + hackathon = models.ForeignKey(Hackathon, on_delete=models.CASCADE) name = models.CharField(max_length=64) entry_code = models.CharField(max_length=64) @@ -57,12 +57,13 @@ def __str__(self): class Applicant(models.Model): - user = models.OneToOneField(User, related_name="applicant", blank=True, null=True) - hackathon = models.ForeignKey(Hackathon, related_name="applicants") - application = models.ForeignKey(Application, related_name="applicants") - team = models.ForeignKey(ApplicantTeam, related_name="applicants", blank=True, null=True) + user = models.OneToOneField(User, related_name="applicant", blank=True, null=True, on_delete=models.CASCADE) + hackathon = models.ForeignKey(Hackathon, related_name="applicants", on_delete=models.CASCADE) + application = models.ForeignKey(Application, related_name="applicants", on_delete=models.CASCADE) + team = models.ForeignKey(ApplicantTeam, related_name="applicants", blank=True, null=True, on_delete=models.CASCADE) data = models.TextField(blank=True, null=True) github_user_name = models.CharField(max_length=39, unique=True, blank=True, null=True) + address = models.CharField(max_length=128) def __str__(self): return "{}'s Application".format(self.user) diff --git a/madras/local_settings_sample.py b/madras/local_settings_sample.py deleted file mode 100644 index fe00914..0000000 --- a/madras/local_settings_sample.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copy me to local_settings.py ! -import os - -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -SECRET_KEY = "foobar" - -DEBUG = True - -# Database -# https://docs.djangoproject.com/en/1.8/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} diff --git a/madras/settings.py b/madras/settings.py index 213139d..bc2825a 100644 --- a/madras/settings.py +++ b/madras/settings.py @@ -20,12 +20,13 @@ # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ.get("SECRET_KEY") +SECRET_KEY = os.environ.get("SECRET_KEY", "=u!#c-2hid%(4lq3w--$64!%qmbmmo-ae=l2_&*jpf47l84iv4") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.environ.get("DEBUG", "true").lower() == "true" -ALLOWED_HOSTS = [] +# Allow all host headers +ALLOWED_HOSTS = ['*'] # Application definition @@ -107,15 +108,17 @@ # Heroku settings below # Parse database configuration from $DATABASE_URL import dj_database_url -DATABASES = {} -DATABASES['default'] = dj_database_url.config() +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} +DATABASES['default'].update(dj_database_url.config()) # Honor the 'X-Forwarded-Proto' header for request.is_secure() # SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') -# Allow all host headers -ALLOWED_HOSTS = ['*'] - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.9/howto/static-files/ @@ -135,8 +138,3 @@ STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' # SECURE_SSL_REDIRECT = True - -try: - from local_settings import * -except ImportError: - pass diff --git a/requirements.txt b/requirements.txt index a4455c7..c8a83ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ django-toolbelt==0.0.1 djangorestframework==3.6.4 enum34==1.1.6 github3.py==1.0.0a4 +geopy==1.11.0 gunicorn==19.7.1 idna==2.6 ipaddress==1.0.18