diff --git a/jar.sql b/jar.sql new file mode 100644 index 0000000..80a5053 Binary files /dev/null and b/jar.sql differ diff --git a/requirements/formulas.py b/requirements/formulas.py new file mode 100644 index 0000000..60ff1f1 --- /dev/null +++ b/requirements/formulas.py @@ -0,0 +1,137 @@ +#Percentages from http://www.exrx.net/Calculators/OneRepMax.html + +BRZYCKI = {"1": 100, + "2": 95, + "3": 90, + "4": 88, + "5": 86, + "6": 83, + "7": 80, + "8": 78, + "9": 76, + "10": 75, + "11": 72, + "12": 70} + +BEACHLE = {"1": 100, + "2": 95, + "3": 93, + "4": 90, + "5": 87, + "6": 85, + "7": 83, + "8": 80, + "9": 77, + "10": 75, + "11": 75, + "12": 67, + "13": 65} + +DOSREMEDIOS = {"1": 100, + "2": 92, + "3": 90, + "4": 87, + "5": 85, + "6": 82, + "7": 82, + "8": 75, + "9": 75, + "10": 70, + "11": 70, + "12": 65, + "13": 60} + +maxComputeMethod = BRZYCKI + +def compute1rm(set, old1rm=0): + """Compute the 1rm for this exercise. + + set = iterator of the current set. Needs to have reps and weight + as available variables + old1rm = the current 1rm + + Usage + >>> set = _Set() + >>> set.reps = 4 + >>> set.weight = 100 + >>> compute1rm(set) + 88.0 + + >>> compute1rm(set, 4) + 88.0 + + """ + newMax = old1rm + reps = str(set.reps) + + if set.reps < 1: + #0 reps - return old1rm + return old1rm + elif reps not in maxComputeMethod.keys(): + #Reps are not in the dictionary, return the larger of the + #older max and whatever was done in this set + return max((old1rm, set.weight)) + + thisMax = .01 * set.weight * maxComputeMethod[reps] + + if thisMax > newMax: + return thisMax + + if newMax > 0: + return newMax + else: + return set.weight + +def getSetRepValues(reps, points): + """Return rep values for this set. + + Recursive, point values decrease by half for every 10 reps. + + >>> getSetRepValues(1, 10) + 10 + + >>> getSetRepValues(10, 10) + 100 + + >>> getSetRepValues(20, 10) + 150 + + >>> getSetRepValues(30, 10) + 170 + + """ + + if reps > 10: + return points * 10 + getSetRepValues(reps-10, points/2) + else: + return points * reps + +def calculateSetPoints(set, old1rm=0): + """Calculate the points for this set. + """ + pointsEarned = 0 + base1rmPoints = 100 + new1rm = compute1rm(set, old1rm) + + #New 1rm deserves extra points! + if(new1rm > old1rm): + pointsEarned += base1rmPoints/2 + + percentage1rm = set.weight / new1rm + + reps = set.reps + + #First 10 reps get full point value + #Next 10 reps get 50% of that rep value + pointsEarned += getSetRepValues(reps, (percentage1rm * 100)) + + return pointsEarned + +class _Set(object): + exercise = 0 + reps = 0 + weight = 0 + +if "__main__" == __name__: + import doctest + doctest.testmod() diff --git a/requirements/formulas.txt b/requirements/formulas.txt new file mode 100644 index 0000000..fd5ad51 --- /dev/null +++ b/requirements/formulas.txt @@ -0,0 +1,20 @@ +**Lifts** +Base point value of 100 for 1 rep of 1rm. (Initial time doing this lift means the heaviest lift is the 1rm, the rest will be computed based on that.) +Other lower lifts get point value based on percentage, up to 10 reps. After 10 reps, they get 50% of that point value per rep for another 10 reps, etc. +Maybe 150 points for a new 1rm? +Possibly some kind of level up value for breaking into a different level based on strstd standards? + +**Cardio** +I have no idea how to handle this fairly. New people shouldn't be penalized for running only a mile, but a marathon should obviously be worth more. Maybe get some cardio people's input on how to handle this fairly. + +**HIIT** +I feel like this needs it's own category. Going by distance and speed doesn't make sense for hill sprints and such. Under this category: Prowler push, hill sprint, bike HIIT. + +Again, 100 point baseline for time/distance. Break either of these to get the 50pt 1rm+ type calculation for the session. + +**Bodyweight Work** +This would include yoga and gymnastics. + +Yoga works through a progression, so maybe we could handle this like a complex? A specific hold wouldn't be timed, but you could look at how many times you did a set of positions in a yoga workout. We could use a yoga expert for hold names - some people use the English name and some the ... Sandskrit? ... name. 100pts for a complex, bonus for spending longer on it? Maybe something could be figured out for progression of hold difficulty (crow stand vs hand stand). + +Gymnastics is a little more straightforward - holds for time are easier to figure out. 100pts for doing a hold for the 1rm time, 50pt bonus for going up by ... say ... 30 seconds? \ No newline at end of file diff --git a/requirements/graphics.txt b/requirements/graphics.txt new file mode 100644 index 0000000..524ec9a --- /dev/null +++ b/requirements/graphics.txt @@ -0,0 +1,20 @@ +Levels + +Let's say that we start with a limit of level 10. +This would mean we need 10 pieces of avitar art for male and 10 for female. + +0 Avitar is a scrawny guy/girl in a loincloth or similar, a little hunched over. +1 Avatar gains becomes tone. +2 Avatar gains leather armor and boots. +3 Avatar gains more muscle. +4 Avatar gains a helmet. +5 Avatar stands up straight. +6 Avatar gains a sword. +7 Avatar gains more muscle. +8 Avatar gains a shield. +9 Avatar gains hero stance (like He-Man). +10 Avatar gains a cape. + +We could do something similar for guild levels, maybe with a Viking ship that ends up with fancy shields, a masthead, and sails. + +We could also have participation badges for people that aren't necessarily progressing very much. Maybe a crow for someone that goes out and does a lot of different things, a bag of goald for someone that participates every few days even if they don't gain many points, etc. \ No newline at end of file diff --git a/requirements/testFormulas.py b/requirements/testFormulas.py new file mode 100644 index 0000000..e0e47bf --- /dev/null +++ b/requirements/testFormulas.py @@ -0,0 +1,95 @@ +import unittest +from formulas import * + +class Set(object): + reps = 0 + weight = 100 + +class TestCalculateSetPoints(unittest.TestCase): + + def setup(self): + self.set = Set() + self.set.reps = 0 + self.set.weight = 100 + + def test0reps(self): + """0 reps should earn 0 points""" + self.set.reps = 0 + points = calculateSetPoints(set, 45) + self.assert(points, 0, "0 points should have been earned") + + def test1reps(self): + """1 rep should earn the full amount""" + self.set.reps = 1 + points = calculateSetPoints(set, 10) + self.assert(points, 10, "10 points should have been earned") + + def test10reps(self): + """10 reps should earn 10xpoints points""" + self.set.reps = 10 + points = calculateSetPoints(set, 10) + self.assert(points, 100, "100 points should have been earned") + + def test20reps(self): + """20 reps should earn 10xpoints + 10xpoints/2 points""" + self.set.reps = 20 + points = calculateSetPoints(set, 10) + self.assert(points, 150, "150 points should have been earned") + + def test30reps(self): + """30 reps should earn 20 reps points + 10xpoints/2/2 + points""" + self.set.reps = 30 + points = calculateSetPoints(set, 10) + self.assert(points, 170, "170 points should have been earned") + +class TestCompute1rm(unittest.TestCase): + + def setUp(self): + self.set = Set() + self.set.reps = 1 + self.set.weight = 100 + + def test0reps(self): + """Sanity check: + 0 reps should return whatever the old rep max was. + The set is considered incomplete. + """ + self.set.reps = 0 + new1rm = compute1rm(self.set) + self.assertEqual(new1rm, 0, "1rm should be 0 - set was not completed") + newer1rm = compute1rm(self.set, 100) + self.assertEqual(newer1rm, 100, "1rm should remain 100") + + def test1rep(self): + """Sanity check: + 1 rep will always change the max to that weight if it's + more than the old 1rm. If it's not, the 1rm remains + unchanged. + """ + self.set.reps = 1 + new1rm = compute1rm(self.set, 100) + self.assertEqual(new1rm, 100, "1rm should remain 100") + newer1rm = compute1rm(self.set, 99) + self.assertEqual(newer1rm, 100, "1rm should now be 100") + + def testSeveralReps(self): + """Formula check""" + maxComputedMethod = BRZYCKI + self.set.reps = 2 + twoRepsMax = compute1rm(self.set) + self.assertEqual(twoRepsMax, 95, "1rm should now be 95") + + def testWayTooManyReps(self): + """Sanity check: + Lots of reps means the calculators become fairly useless. + We can go ahead and change the max to whatever was done, + but there's no real value in trying to figure out what the + max would be. + """ + self.set.reps = 200 + twohundredRepsMax = compute1rm(self.set) + self.assertEqual(twohundredRepsMax, 100, "1rm should now be 100") + +if "__main__" == __name__: + unittest.main() diff --git a/requirements/user_stories.txt b/requirements/user_stories.txt new file mode 100644 index 0000000..1bf1094 --- /dev/null +++ b/requirements/user_stories.txt @@ -0,0 +1,61 @@ +**Alpha Create a user** +New user receives an email/msg invite from us. +User clicks the link and is presented with new user page. +User chooses a character name and clicks a button to make sure it doesn't already exist. +User chooses metric/imperial default option. +User selects a gender. +User enters their email address. +User clicks save/submit button to create their account. + +**User chooses a guild** +User receives an invitation from a guild to join. +User visits guild page to see what the guild is about. +User clicks "Join Guild" button. +User is directed back to the guild page with a welcome message and member only view. + +**User chooses a guild** +User browses the list of guilds. +User finds a guild they want to join. (Are some guilds invite only?) +User clicks "Join Guild" button. +User is directed back to the guild page with a welcome message and member only view. + +**User enters a workout sequence** +User clicks the "Workout sequence" button. +User selects 5/3/1 BBB. +If user has already done the required exercises, 1rm's are automatically calculated. They are otherwise blank. +User has the ability to adjust 1rm numbers. +User has the ability to change assistive work. +User clicks the "Save" button. +User is presented with confirmation that the sequence was saved. + +**User enters a custom workout sequence** +User clicks the "Workout sequence" button. +User selects "Custom". +User gives the sequence a name. +User enters excercises and goal weights/sets/reps if desired. +User can create new workouts in the sequence by clicking the "New Workout" button. +User clicks the "Save" button. +User is presented with confirmation that the sequence was saved. + +**User enters a workout (5/3/1 was set up as a sequence)** +User clicks the "Track" tab/button/link. +The next appropriate 5/3/1 workout is displayed with appropriate weights and reps. +User adjusts weights and reps as needed. +User has the ability to add or delete assistive work and sets of main work. +User clicks the "Save" button. +User is presented with the visual of their workout along with point information. + +**Guild creates a workout sequence** +Guild admin clicks "Create a workout sequence" from within the guild admin page. +Guild admin gives the workout sequence a name. +Guild admin enters exercises, target sets and reps, and target 1rm percentages or actual weights (not recommended because of gender differences). +Guild admin can create different workouts in the sequence by clicking the "New Workout" button. +Guild admin clicks "Save". +Guild admin is presented with save confirmation, along with a visual of what kind of point percentages will be given for that workout sequence. +Workout sequence now appears in the available list for all users in the guild. + +**User enters a stand-alone workout** +User clicks the "Track" button/link/tab. +User selects exercises and enters sets/reps/assisted/weighted/weight/time information. +User clicks the "Save" button. +User is presented with save confirmation and a graphic representing points/percentages and such earned in this workout. \ No newline at end of file diff --git a/rpf/admin.py b/rpf/admin.py index bc49abf..d72ce04 100644 --- a/rpf/admin.py +++ b/rpf/admin.py @@ -1,9 +1,9 @@ from django.contrib import admin # import all the models from the RPF -from jarnheim.rpf.models import * +from Jarnheim.rpf.models import * admin.site.register(race) admin.site.register(ban) admin.site.register(character) -admin.site.register(guild) \ No newline at end of file +admin.site.register(guild) diff --git a/rpf/guild.py b/rpf/guild.py index 2208a86..1b0a645 100644 --- a/rpf/guild.py +++ b/rpf/guild.py @@ -3,7 +3,8 @@ class guild( models.Model ): id = models.AutoField( primary_key = True ) guild_leader_character = models.ForeignKey( 'character' ) - create_dtm = models.DateTimeField() + create_dtm = models.DateTimeField(verbose_name="Date created", + db_index=False) name = models.CharField( max_length = 30 ) @@ -12,4 +13,4 @@ def __unicode__( self ): class Meta: db_table = 'rpf_guild' - app_label= 'rpf' \ No newline at end of file + app_label= 'rpf' diff --git a/rpf/models.py b/rpf/models.py new file mode 100644 index 0000000..7a8fc37 --- /dev/null +++ b/rpf/models.py @@ -0,0 +1,5 @@ +#from jarnheim.rpf.models import * +from Jarnheim.rpf.race import * +from Jarnheim.rpf.ban import * +from Jarnheim.rpf.character import * +from Jarnheim.rpf.guild import * diff --git a/rpf/race.py b/rpf/race.py index 4f6eeea..6946a82 100644 --- a/rpf/race.py +++ b/rpf/race.py @@ -2,12 +2,14 @@ class race( models.Model ): id = models.AutoField( primary_key = True ) - create_dtm = models.DateTimeField() - name = models.CharField( max_length = 30 ) + create_dtm = models.DateTimeField(verbose_name="Date created", + db_index=False) + name = models.CharField( max_length = 30, + db_index=False) def __unicode__( self ): return self.name class Meta: db_table = 'rpf_race' - app_label= 'rpf' \ No newline at end of file + app_label= 'rpf' diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..1db3450 --- /dev/null +++ b/settings.py @@ -0,0 +1,145 @@ +# Django settings for clothing_exchange project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@example.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': 'jar.sql', # Or path to database file if using sqlite3. + 'USER': '', # Not used with sqlite3. + 'PASSWORD': '', # Not used with sqlite3. + 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '', # Set to empty string for default. Not used with sqlite3. + } +} + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Chicago' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/media/" +MEDIA_ROOT = '/Users/tanglisha/sites/Jarnheim/MEDIA' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '' + +# Absolute path to the directory that holds static files. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = '/Users/tanglisha/Sites/Jarnheim/satic' + +# URL that handles the static files served from STATIC_ROOT. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# URL prefix for admin media -- CSS, JavaScript and images. +# Make sure to use a trailing slash. +# Examples: "http://foo.com/static/admin/", "/static/admin/". +ADMIN_MEDIA_PREFIX = '/static/admin/' + +# A list of locations of additional static files +STATICFILES_DIRS = () + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'al4m%u+xlozu4974whdrayehriuh_^uvvdrip)#nhdfauyseouaidh#=jd^znq-5' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +) + +ROOT_URLCONF = 'Jarnheim.urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + "/Users/tanglisha/Sites/Jarnheim/django_templates", +) + +# Use my user model, rather than the default +AUTH_PROFILE_MODEL='jar_user.Profile' + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # Uncomment the next line to enable the admin: + 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', + + 'rpf', +) + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'loggers': { + 'django.request':{ + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +}