def processor(request): language = load_language() full_path = request.get_full_path() i18n_path = {} for lang in settings.LANGUAGES: i18n_path[lang[0]] = '/{0}{1}'.format(lang[0], full_path[3:]) context = { # Application version 'version': get_version(), # User language 'language': language, # Available application languages 'languages': settings.LANGUAGES, # The current path 'request_full_path': full_path, # Translation links 'i18n_path': i18n_path, # Translation links 'datepicker_i18n_path': 'js/bootstrap-datepicker/locales/bootstrap-datepicker.{0}.js'. format(language.short_name), # Flag for guest users 'has_demo_data': request.session.get('has_demo_data', False), # Don't show messages on AJAX requests (they are deleted if shown) 'no_messages': request.META.get('HTTP_X_WGER_NO_MESSAGES', False), # Default cache time for template fragment caching 'cache_timeout': settings.CACHES['default']['TIMEOUT'] } # Pseudo-intelligent navigation here if '/software/' in request.get_full_path() \ or '/contact' in request.get_full_path() \ or '/api/v2' in request.get_full_path(): context['active_tab'] = constants.SOFTWARE_TAB elif '/exercise/' in request.get_full_path(): context['active_tab'] = constants.EXERCISE_TAB elif '/nutrition/' in request.get_full_path(): context['active_tab'] = constants.NUTRITION_TAB elif '/weight/' in request.get_full_path(): context['active_tab'] = constants.WEIGHT_TAB elif '/workout/' in request.get_full_path(): context['active_tab'] = constants.WORKOUT_TAB else: context['active_tab'] = constants.USER_TAB return context
def process_options(argv=None): if argv is None: argv = sys.argv[1:] parser = optparse.OptionParser( description="Run wger Workout Manager using django's builtin server") parser.add_option("-a", "--address", help="IP Address to listen on.") parser.add_option("-p", "--port", type="int", help="Port to listen on.") parser.add_option( "--syncdb", action="store_true", help="Update/create database before starting the server.") parser.add_option( "--reset-admin", action="store_true", help="Make sure the user 'admin' exists and uses 'admin' as password.") parser.add_option("-s", "--settings", help="Path to the wger configuration file.") parser.add_option("--no-reload", action="store_true", help="Do not reload the development server.") parser.add_option("--migrate-db", action="store_true", help="Runs all database migrations (safe operation).") parser.add_option("--version", action="store_true", help="Show version and exit.") parser.add_option("--show-config", action="store_true", help="Show configuration paths and exit.") opts, args = parser.parse_args(argv) if opts.version: print get_version() exit(0) if opts.show_config: print "Settings file: %s" % get_user_config_path('wger', 'settings.py') print "Database file: %s" % get_user_data_path('wger', 'database.sqlite') exit(0) if args: sys.stderr.write("This command does not take arguments!\n\n") parser.print_help() sys.exit(1) return opts
def get_calendar(): """ Creates and returns a calendar object :return: Calendar """ calendar = Calendar() calendar.add("prodid", "-//wger Workout Manager//wger.de//") calendar.add("version", get_version()) return calendar
def get_calendar(): ''' Creates and returns a calendar object :return: Calendar ''' calendar = Calendar() calendar.add('prodid', '-//wger Workout Manager//wger.de//') calendar.add('version', get_version()) return calendar
def process_options(argv=None): if argv is None: argv = sys.argv[1:] parser = optparse.OptionParser( description="Run wger Workout Manager using django's builtin server") parser.add_option("-a", "--address", help="IP Address to listen on.") parser.add_option("-p", "--port", type="int", help="Port to listen on.") parser.add_option( "--syncdb", action="store_true", help="Update/create database before starting the server.") parser.add_option( "--reset-admin", action="store_true", help="Make sure the user 'admin' exists and uses 'admin' as password.") parser.add_option( "-s", "--settings", help="Path to the wger configuration file.") parser.add_option( "--no-reload", action="store_true", help="Do not reload the development server.") parser.add_option( "--migrate-db", action="store_true", help="Runs all database migrations (safe operation).") parser.add_option( "--version", action="store_true", help="Show version and exit.") parser.add_option( "--show-config", action="store_true", help="Show configuration paths and exit.") opts, args = parser.parse_args(argv) if opts.version: print get_version() exit(0) if opts.show_config: print "Settings file: %s" % get_user_config_path('wger', 'settings.py') print "Database file: %s" % get_user_data_path('wger', 'database.sqlite') exit(0) if args: sys.stderr.write("This command does not take arguments!\n\n") parser.print_help() sys.exit(1) return opts
def processor(request): full_path = request.get_full_path() context = { # Application version 'version': get_version(), # User language 'language': load_language(), # The current path 'request_full_path': full_path, # Translation links 'i18n_path': { 'de': '/de' + full_path[3:], 'en': '/en' + full_path[3:] }, # Contact email 'contact_email': 'roland @ geider.net', # Flag for guest users 'has_demo_data': request.session.get('has_demo_data', False), # Don't show messages on AJAX requests (they are deleted if shown) 'no_messages': request.META.get('HTTP_X_WGER_NO_MESSAGES', False), } # Pseudo-intelligent navigation here if '/software/' in request.get_full_path() \ or '/contact' in request.get_full_path(): context['active_tab'] = constants.SOFTWARE_TAB elif '/exercise/' in request.get_full_path(): context['active_tab'] = constants.EXERCISE_TAB elif '/nutrition/' in request.get_full_path(): context['active_tab'] = constants.NUTRITION_TAB elif '/weight/' in request.get_full_path(): context['active_tab'] = constants.WEIGHT_TAB elif '/workout/' in request.get_full_path(): context['active_tab'] = constants.WORKOUT_TAB else: context['active_tab'] = constants.USER_TAB return context
def processor(request): full_path = request.get_full_path() context = { # Application version 'version': get_version(), # User language 'language': load_language(), # The current path 'request_full_path': full_path, # Translation links 'i18n_path': {'de': '/de' + full_path[3:], 'en': '/en' + full_path[3:]}, # Contact email 'contact_email': 'roland @ geider.net', # Flag for guest users 'has_demo_data': request.session.get('has_demo_data', False), # Don't show messages on AJAX requests (they are deleted if shown) 'no_messages': request.META.get('HTTP_X_WGER_NO_MESSAGES', False), } # Pseudo-intelligent navigation here if '/software/' in request.get_full_path() \ or '/contact' in request.get_full_path(): context['active_tab'] = constants.SOFTWARE_TAB elif '/exercise/' in request.get_full_path(): context['active_tab'] = constants.EXERCISE_TAB elif '/nutrition/' in request.get_full_path(): context['active_tab'] = constants.NUTRITION_TAB elif '/weight/' in request.get_full_path(): context['active_tab'] = constants.WEIGHT_TAB elif '/workout/' in request.get_full_path(): context['active_tab'] = constants.WORKOUT_TAB else: context['active_tab'] = constants.USER_TAB return context
def render_footer(url, date=None): ''' Renders the footer used in the different PDFs :return: a Paragraph object ''' if not date: date = datetime.date.today().strftime("%d.%m.%Y") p = Paragraph('''<para> {date} - <a href="{url}">{url}</a> - wger Workout Manager {version} </para>'''.format(date=date, url=url, version=get_version()), styleSheet["Normal"]) return p
def export_pdf(request, id): ''' Generates a PDF with the contents of a nutrition plan See also * http://www.blog.pythonlibrary.org/2010/09/21/reportlab * http://www.reportlab.com/apis/reportlab/dev/platypus.html ''' #Load the workout plan = get_object_or_404(NutritionPlan, pk=id, user=request.user) # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(mimetype='application/pdf') # Translators: translation can only have ASCII characters response['Content-Disposition'] = 'attachment; filename=%s.pdf' % _('nutritional-plan') # Create the PDF object, using the response object as its "file." doc = SimpleDocTemplate(response, pagesize=A4, title=_('Workout'), author='wger Workout Manager', subject=_('Nutritional plan %s') % request.user.username) # Background colour for header # Reportlab doesn't use the HTML hexadecimal format, but has a range of # 0 till 1, so we have to convert here. header_colour = colors.Color(int('73', 16) / 255.0, int('8a', 16) / 255.0, int('5f', 16) / 255.0) # container for the 'Flowable' objects elements = [] data = [] # Iterate through the Plan meal_markers = [] ingredient_markers = [] # Meals i = 0 for meal in plan.meal_set.select_related(): i += 1 meal_markers.append(len(data)) if not meal.time: P = Paragraph('<para align="center"><strong>%(meal_nr)s</strong></para>' % {'meal_nr': i}, styleSheet["Normal"]) else: P = Paragraph('<para align="center"><strong>%(meal_nr)s - ' '%(meal_time)s</strong></para>' % {'meal_nr': i, 'meal_time': meal.time.strftime("%H:%M")}, styleSheet["Normal"]) data.append([P]) # Ingredients for item in meal.mealitem_set.select_related(): ingredient_markers.append(len(data)) P = Paragraph('<para>%s</para>' % item.ingredient.name, styleSheet["Normal"]) if item.get_unit_type() == MEALITEM_WEIGHT_GRAM: unit_name = 'g' else: unit_name = ' ' + item.weight_unit.unit.name data.append(["{0}{1}".format(item.amount, unit_name), P]) # Set general table styles #('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), #('BOX', (0,0), (-1,-1), 0.25, colors.black) table_style = [] # Set specific styles, e.g. background for title cells for marker in meal_markers: # Set background colour for headings table_style.append(('BACKGROUND', (0, marker), (-1, marker), header_colour)) table_style.append(('BOX', (0, marker), (-1, marker), 1.25, colors.black)) # Make the headings span the whole width table_style.append(('SPAN', (0, marker), (-1, marker))) # has the plan any data? if data: t = Table(data, style=table_style) # Manually set the width of the columns t._argW[0] = 2 * cm # There is nothing to output else: t = Paragraph(_('<i>This is an empty plan, what did you expect on the PDF?</i>'), styleSheet["Normal"]) # Set the title (if available) if plan.description: P = Paragraph('<para align="center"><strong>%(description)s</strong></para>' % {'description': plan.description}, styleSheet["Normal"]) elements.append(P) # Filler P = Paragraph('<para> </para>', styleSheet["Normal"]) elements.append(P) # append the table to the document elements.append(t) # Footer, add filler paragraph P = Paragraph('<para> </para>', styleSheet["Normal"]) elements.append(P) # Print date and info created = datetime.date.today().strftime("%d.%m.%Y") url = reverse('wger.nutrition.views.plan.view', kwargs={'id': plan.id}) P = Paragraph('''<para align="left"> %(date)s - <a href="%(url)s">%(url)s</a> - %(created)s %(version)s </para>''' % {'date': _("Created on the <b>%s</b>") % created, 'created': "wger Workout Manager", 'version': get_version(), 'url': request.build_absolute_uri(url), }, styleSheet["Normal"]) elements.append(P) doc.build(elements) return response
def handle(self, **options): if not settings.MEDIA_ROOT: raise ImproperlyConfigured( 'Please set MEDIA_ROOT in your settings file') remote_url = options['remote_url'] try: val = URLValidator() val(remote_url) except ValidationError: raise CommandError('Please enter a valid URL') headers = { 'User-agent': default_user_agent('wger/{} + requests'.format(get_version())) } # Get all exercises result = requests.get(EXERCISE_API.format(remote_url), headers=headers).json() for exercise_json in result['results']: exercise_name = exercise_json['name'] exercise_uuid = exercise_json['uuid'] exercise_id = exercise_json['id'] self.stdout.write('') self.stdout.write( f"*** {exercise_name} (ID: {exercise_id}, UUID: {exercise_uuid})" ) try: exercise = Exercise.objects.get(uuid=exercise_uuid) except Exercise.DoesNotExist: self.stdout.write( ' Remote exercise not found in local DB, skipping...') continue # Get all images images = requests.get(IMAGE_API.format(remote_url, exercise_id), headers=headers).json() if images['count']: for image_json in images['results']: image_id = image_json['id'] image_uuid = image_json['uuid'] result = requests.get(THUMBNAIL_API.format( remote_url, image_id), headers=headers).json() image_name = os.path.basename(result['original']) self.stdout.write(' Fetching image {0} - {1}'.format( image_id, image_name)) try: image = ExerciseImage.objects.get(uuid=image_uuid) self.stdout.write( ' --> Image already present locally, skipping...' ) continue except ExerciseImage.DoesNotExist: self.stdout.write( ' --> Image not found in local DB, creating now...' ) image = ExerciseImage() image.uuid = image_uuid # Save the downloaded image # http://stackoverflow.com/questions/1308386/programmatically-saving-image-to- retrieved_image = requests.get(remote_url + result['original'], headers=headers) # Temporary files on windows don't support the delete attribute if os.name == 'nt': img_temp = NamedTemporaryFile() else: img_temp = NamedTemporaryFile(delete=True) img_temp.write(retrieved_image.content) img_temp.flush() image.exercise = exercise.exercise_base image.is_main = image_json['is_main'] image.status = image_json['status'] image.image.save( os.path.basename(image_name), File(img_temp), ) image.save() else: self.stdout.write( ' No images for this exercise, nothing to do')
# -*- coding: utf-8 -*- # This file is part of wger Workout Manager. # # wger Workout Manager is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # wger Workout Manager is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Workout Manager. If not, see <http://www.gnu.org/licenses/>. # wger from wger import get_version VERSION = get_version() default_app_config = 'wger.nutrition.apps.NutritionConfig'
def handle(self, **options): if not settings.MEDIA_ROOT: raise ImproperlyConfigured('Please set MEDIA_ROOT in your settings file') remote_url = options['remote_url'] try: val = URLValidator() val(remote_url) except ValidationError: raise CommandError('Please enter a valid URL') headers = {'User-agent': default_user_agent('wger/{} + requests'.format(get_version()))} # Get all images page = 1 all_images_processed = False result = requests.get(IMAGE_API.format(remote_url), headers=headers).json() self.stdout.write('*** Processing images ***') while not all_images_processed: self.stdout.write('') self.stdout.write(f'*** Page {page}') self.stdout.write('') page += 1 if result['next']: result = requests.get(result['next'], headers=headers).json() else: all_images_processed = True for image_data in result['results']: image_uuid = image_data['uuid'] self.stdout.write(f'Processing image {image_uuid}') try: exercise_base = ExerciseBase.objects.get(id=image_data['exercise']) except ExerciseBase.DoesNotExist: self.stdout.write(' Remote exercise base not found in local DB, skipping...') continue try: image = ExerciseImage.objects.get(uuid=image_uuid) self.stdout.write(' Image already present locally, skipping...') continue except ExerciseImage.DoesNotExist: self.stdout.write(' Image not found in local DB, creating now...') image = ExerciseImage() image.uuid = image_uuid # Save the downloaded image # http://stackoverflow.com/questions/1308386/programmatically-saving-image-to- retrieved_image = requests.get(image_data['image'], headers=headers) # Temporary files on windows don't support the delete attribute if os.name == 'nt': img_temp = NamedTemporaryFile() else: img_temp = NamedTemporaryFile(delete=True) img_temp.write(retrieved_image.content) img_temp.flush() image.exercise = exercise_base image.is_main = image_data['is_main'] image.status = image_data['status'] image.image.save( os.path.basename(os.path.basename(image_data['image'])), File(img_temp), ) image.save() self.stdout.write(self.style.SUCCESS(' successfully saved'))
def export_pdf(request, id, uidb64=None, token=None): ''' Generates a PDF with the contents of a nutrition plan See also * http://www.blog.pythonlibrary.org/2010/09/21/reportlab * http://www.reportlab.com/apis/reportlab/dev/platypus.html ''' # Load the plan if uidb64 is not None and token is not None: if check_token(uidb64, token): plan = get_object_or_404(NutritionPlan, pk=id) else: return HttpResponseForbidden() else: if request.user.is_anonymous(): return HttpResponseForbidden() plan = get_object_or_404(NutritionPlan, pk=id, user=request.user) plan_data = plan.get_nutritional_values() # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(content_type='application/pdf') # Create the PDF object, using the response object as its "file." doc = SimpleDocTemplate(response, pagesize=A4, title=_('Workout'), author='wger Workout Manager', subject=_('Nutritional plan %s') % request.user.username) # Background colour for header # Reportlab doesn't use the HTML hexadecimal format, but has a range of # 0 till 1, so we have to convert here. header_colour = colors.Color( int('73', 16) / 255.0, int('8a', 16) / 255.0, int('5f', 16) / 255.0) # container for the 'Flowable' objects elements = [] data = [] # Iterate through the Plan meal_markers = [] ingredient_markers = [] # Meals i = 0 for meal in plan.meal_set.select_related(): i += 1 meal_markers.append(len(data)) if not meal.time: p = Paragraph( u'<para align="center"><strong>{nr} {meal_nr}</strong></para>'. format(nr=_('Nr.'), meal_nr=i), styleSheet["Normal"]) else: p = Paragraph( u'<para align="center"><strong>' u'{nr} {meal_nr} - {meal_time}' u'</strong></para>'.format( nr=_('Nr.'), meal_nr=i, meal_time=meal.time.strftime("%H:%M")), styleSheet["Normal"]) data.append([p]) # Ingredients for item in meal.mealitem_set.select_related(): ingredient_markers.append(len(data)) p = Paragraph(u'<para>{0}</para>'.format(item.ingredient.name), styleSheet["Normal"]) if item.get_unit_type() == MEALITEM_WEIGHT_GRAM: unit_name = 'g' else: unit_name = ' ' + item.weight_unit.unit.name data.append([ Paragraph(u"{0}{1}".format(item.amount, unit_name), styleSheet["Normal"]), p ]) # Set general table styles table_style = [] # Set specific styles, e.g. background for title cells for marker in meal_markers: # Set background colour for headings table_style.append( ('BACKGROUND', (0, marker), (-1, marker), header_colour)) table_style.append( ('BOX', (0, marker), (-1, marker), 1.25, colors.black)) # Make the headings span the whole width table_style.append(('SPAN', (0, marker), (-1, marker))) # has the plan any data? if data: t = Table(data, style=table_style) # Manually set the width of the columns t._argW[0] = 2.5 * cm # There is nothing to output else: t = Paragraph( _('<i>This is an empty plan, what did you expect on the PDF?</i>'), styleSheet["Normal"]) # Set the title (if available) if plan.description: p = Paragraph( '<para align="center"><strong>%(description)s</strong></para>' % {'description': plan.description}, styleSheet["Bold"]) elements.append(p) # Filler elements.append(Spacer(10 * cm, 0.5 * cm)) # append the table to the document elements.append(t) elements.append(Paragraph('<para> </para>', styleSheet["Normal"])) # Create table with nutritional calculations data = [] data.append([ Paragraph( u'<para align="center">{0}</para>'.format(_('Nutritional data')), styleSheet["Bold"]) ]) data.append([ Paragraph(_('Macronutrients'), styleSheet["Normal"]), Paragraph(_('Total'), styleSheet["Normal"]), Paragraph(_('Percent of energy'), styleSheet["Normal"]), Paragraph(_('g per body kg'), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Energy'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['energy']), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Protein'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['protein']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['percent']['protein']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['per_kg']['protein']), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Carbohydrates'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['carbohydrates']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['percent']['carbohydrates']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['per_kg']['carbohydrates']), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Sugar content in carbohydrates'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['carbohydrates_sugar']), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Fat'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['fat']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['percent']['fat']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['per_kg']['fat']), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Saturated fat content in fats'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['fat_saturated']), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Fibres'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['fibres']), styleSheet["Normal"]) ]) data.append([ Paragraph(_('Sodium'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['sodium']), styleSheet["Normal"]) ]) table_style = [] table_style.append(('BOX', (0, 0), (-1, -1), 1.25, colors.black)) table_style.append(('GRID', (0, 0), (-1, -1), 0.40, colors.black)) table_style.append(('SPAN', (0, 0), (-1, 0))) # Title table_style.append(('SPAN', (1, 2), (-1, 2))) # Energy table_style.append(('SPAN', (1, 5), (-1, 5))) # Sugar table_style.append(('SPAN', (1, 7), (-1, 7))) # Saturated fats table_style.append(('SPAN', (1, 8), (-1, 8))) # Fibres table_style.append(('SPAN', (1, 9), (-1, 9))) # Sodium t = Table(data, style=table_style) t._argW[0] = 5 * cm elements.append(t) # Footer, date and info elements.append(Spacer(10 * cm, 0.5 * cm)) created = datetime.date.today().strftime("%d.%m.%Y") url = reverse('nutrition-view', kwargs={'id': plan.id}) p = Paragraph( '''<para align="left"> %(date)s - <a href="%(url)s">%(url)s</a> - %(created)s %(version)s </para>''' % { 'date': _("Created on the <b>%s</b>") % created, 'created': "wger Workout Manager", 'version': get_version(), 'url': request.build_absolute_uri(url), }, styleSheet["Normal"]) elements.append(p) doc.build(elements) response[ 'Content-Disposition'] = 'attachment; filename=nutritional-plan.pdf' response['Content-Length'] = len(response.content) return response
def processor(request): language = load_language() full_path = request.get_full_path() i18n_path = {} static_path = static('images/logos/logo-social.png') for lang in settings.LANGUAGES: i18n_path[lang[0]] = '/{0}{1}'.format(lang[0], full_path[3:]) context = { # Application version 'version': get_version(), # Twitter handle for this instance 'twitter': settings.WGER_SETTINGS['TWITTER'], # User language 'language': language, # Available application languages 'languages': settings.LANGUAGES, # The current path 'request_full_path': full_path, # The current full path with host 'request_absolute_path': request.build_absolute_uri(), 'image_absolute_path': request.build_absolute_uri(static_path), # Translation links 'i18n_path': i18n_path, # Flag for guest users 'has_demo_data': request.session.get('has_demo_data', False), # Don't show messages on AJAX requests (they are deleted if shown) 'no_messages': request.META.get('HTTP_X_WGER_NO_MESSAGES', False), # Default cache time for template fragment caching 'cache_timeout': settings.CACHES['default']['TIMEOUT'], # Used for logged in trainers 'trainer_identity': request.session.get('trainer.identity'), # current gym, if available 'custom_header': get_custom_header(request), } # Pseudo-intelligent navigation here if '/software/' in request.get_full_path() \ or '/contact' in request.get_full_path() \ or '/api/v2' in request.get_full_path(): context['active_tab'] = constants.SOFTWARE_TAB context['show_shariff'] = True elif '/exercise/' in request.get_full_path(): context['active_tab'] = constants.WORKOUT_TAB elif '/nutrition/' in request.get_full_path(): context['active_tab'] = constants.NUTRITION_TAB elif '/weight/' in request.get_full_path(): context['active_tab'] = constants.WEIGHT_TAB elif '/workout/' in request.get_full_path(): context['active_tab'] = constants.WORKOUT_TAB return context
# -*- coding: utf-8 -*- # This file is part of wger Workout Manager. # # wger Workout Manager is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # wger Workout Manager is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with Workout Manager. If not, see <http://www.gnu.org/licenses/>. from wger import get_version VERSION = get_version() default_app_config = 'wger.email.apps.Config'
def handle(self, **options): remote_url = options['remote_url'] try: val = URLValidator() val(remote_url) except ValidationError: raise CommandError('Please enter a valid URL') headers = { 'User-agent': default_user_agent('wger/{} + requests'.format(get_version())) } # # Categories # self.stdout.write('*** Synchronizing categories...') result = requests.get(CATEGORY_API.format(remote_url), headers=headers).json() for category_data in result['results']: category_id = category_data['id'] category_name = category_data['name'] try: category = ExerciseCategory.objects.get(pk=category_id) category.name = category_name category.save() except ExerciseCategory.DoesNotExist: self.stdout.write( self.style.WARNING(f'Saving new category {category_name}')) category = ExerciseCategory(id=category_id, name=category_name) category.save() self.stdout.write(self.style.SUCCESS('done!\n')) # # Muscles # self.stdout.write('*** Synchronizing muscles...') result = requests.get(MUSCLE_API.format(remote_url), headers=headers).json() for muscle_data in result['results']: muscle_id = muscle_data['id'] muscle_name = muscle_data['name'] muscle_is_front = muscle_data['is_front'] muscle_url_main = muscle_data['image_url_main'] muscle_url_secondary = muscle_data['image_url_secondary'] try: muscle = Muscle.objects.get(pk=muscle_id) muscle.name = muscle_name muscle_is_front = muscle_is_front muscle.save() except Muscle.DoesNotExist: muscle = Muscle(id=muscle_id, name=muscle_name, is_front=muscle_is_front) muscle.save() self.stdout.write( self.style.WARNING( f'Saved new muscle {muscle_name}. ' f'Save the corresponding images manually')) self.stdout.write(self.style.WARNING(muscle_url_main)) self.stdout.write(self.style.WARNING(muscle_url_secondary)) self.stdout.write(self.style.SUCCESS('done!\n')) # # Equipment # self.stdout.write('*** Synchronizing equipment...') result = requests.get(EQUIPMENT_API.format(remote_url), headers=headers).json() for equipment_data in result['results']: equipment_id = equipment_data['id'] equipment_name = equipment_data['name'] try: equipment = Equipment.objects.get(pk=equipment_id) equipment.name = equipment_name equipment.save() except Equipment.DoesNotExist: self.stdout.write(f'Saved new equipment {equipment_name}') equipment = Equipment(id=equipment_id, name=equipment_name) equipment.save() self.stdout.write(self.style.SUCCESS('done!\n')) # # Exercises # self.stdout.write('*** Synchronizing exercises...') page = 1 all_exercise_processed = False result = requests.get(EXERCISE_API.format(remote_url), headers=headers).json() while not all_exercise_processed: for data in result['results']: exercise_uuid = data['uuid'] exercise_name = data['name'] exercise_description = data['description'] equipment = [ Equipment.objects.get(pk=i['id']) for i in data['equipment'] ] muscles = [ Muscle.objects.get(pk=i['id']) for i in data['muscles'] ] muscles_sec = [ Muscle.objects.get(pk=i['id']) for i in data['muscles_secondary'] ] try: exercise = Exercise.objects.get(uuid=exercise_uuid) exercise.name = exercise_name exercise.description = exercise_description # Note: this should not happen and is an unnecessary workaround # https://github.com/wger-project/wger/issues/840 if not exercise.exercise_base: warning = f'Exercise {exercise.uuid} has no base, this should not happen!' \ f'Skipping...\n' self.stdout.write(self.style.WARNING(warning)) continue exercise.exercise_base.category_id = data['category']['id'] exercise.exercise_base.muscles.set(muscles) exercise.exercise_base.muscles_secondary.set(muscles_sec) exercise.exercise_base.equipment.set(equipment) exercise.exercise_base.save() exercise.save() except Exercise.DoesNotExist: self.stdout.write(f'Saved new exercise {exercise_name}') exercise = Exercise( uuid=exercise_uuid, name=exercise_name, description=exercise_description, language_id=data['language']['id'], license_id=data['license']['id'], license_author=data['license_author'], ) base = ExerciseBase() base.category_id = data['category']['id'] base.save() base.muscles.set(muscles) base.muscles_secondary.set(muscles_sec) base.equipment.set(equipment) base.save() exercise.save() if result['next']: page += 1 result = requests.get(result['next'], headers=headers).json() else: all_exercise_processed = True self.stdout.write(self.style.SUCCESS('done!\n'))
def export_pdf(request, id, uidb64=None, token=None): ''' Generates a PDF with the contents of a nutrition plan See also * http://www.blog.pythonlibrary.org/2010/09/21/reportlab * http://www.reportlab.com/apis/reportlab/dev/platypus.html ''' # Load the plan if uidb64 is not None and token is not None: if check_token(uidb64, token): plan = get_object_or_404(NutritionPlan, pk=id) else: return HttpResponseForbidden() else: if request.user.is_anonymous(): return HttpResponseForbidden() plan = get_object_or_404(NutritionPlan, pk=id, user=request.user) plan_data = plan.get_nutritional_values() # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(content_type='application/pdf') # Create the PDF object, using the response object as its "file." doc = SimpleDocTemplate(response, pagesize=A4, title=_('Nutrition plan'), author='wger Workout Manager', subject=_('Nutritional plan %s') % request.user.username) # Background colour for header # Reportlab doesn't use the HTML hexadecimal format, but has a range of # 0 till 1, so we have to convert here. header_colour = colors.Color(int('73', 16) / 255.0, int('8a', 16) / 255.0, int('5f', 16) / 255.0) # container for the 'Flowable' objects elements = [] data = [] # Iterate through the Plan meal_markers = [] ingredient_markers = [] # Meals i = 0 for meal in plan.meal_set.select_related(): i += 1 meal_markers.append(len(data)) if not meal.time: p = Paragraph(u'<para align="center"><strong>{nr} {meal_nr}</strong></para>' .format(nr=_('Nr.'), meal_nr=i), styleSheet["Normal"]) else: p = Paragraph(u'<para align="center"><strong>' u'{nr} {meal_nr} - {meal_time}' u'</strong></para>' .format(nr=_('Nr.'), meal_nr=i, meal_time=meal.time.strftime("%H:%M")), styleSheet["Normal"]) data.append([p]) # Ingredients for item in meal.mealitem_set.select_related(): ingredient_markers.append(len(data)) p = Paragraph(u'<para>{0}</para>'.format(item.ingredient.name), styleSheet["Normal"]) if item.get_unit_type() == MEALITEM_WEIGHT_GRAM: unit_name = 'g' else: unit_name = ' ' + item.weight_unit.unit.name data.append([Paragraph(u"{0}{1}".format(item.amount, unit_name), styleSheet["Normal"]), p]) # Set general table styles table_style = [] # Set specific styles, e.g. background for title cells for marker in meal_markers: # Set background colour for headings table_style.append(('BACKGROUND', (0, marker), (-1, marker), header_colour)) table_style.append(('BOX', (0, marker), (-1, marker), 1.25, colors.black)) # Make the headings span the whole width table_style.append(('SPAN', (0, marker), (-1, marker))) # has the plan any data? if data: t = Table(data, style=table_style) # Manually set the width of the columns t._argW[0] = 2.5 * cm # There is nothing to output else: t = Paragraph(_('<i>This is an empty plan, what did you expect on the PDF?</i>'), styleSheet["Normal"]) # Set the title (if available) if plan.description: p = Paragraph('<para align="center"><strong>%(description)s</strong></para>' % {'description': plan.description}, styleSheet["Bold"]) elements.append(p) # Filler elements.append(Spacer(10 * cm, 0.5 * cm)) # append the table to the document elements.append(t) elements.append(Paragraph('<para> </para>', styleSheet["Normal"])) # Create table with nutritional calculations data = [] data.append([Paragraph(u'<para align="center">{0}</para>'.format(_('Nutritional data')), styleSheet["Bold"])]) data.append([Paragraph(_('Macronutrients'), styleSheet["Normal"]), Paragraph(_('Total'), styleSheet["Normal"]), Paragraph(_('Percent of energy'), styleSheet["Normal"]), Paragraph(_('g per body kg'), styleSheet["Normal"])]) data.append([Paragraph(_('Energy'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['energy']), styleSheet["Normal"])]) data.append([Paragraph(_('Protein'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['protein']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['percent']['protein']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['per_kg']['protein']), styleSheet["Normal"])]) data.append([Paragraph(_('Carbohydrates'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['carbohydrates']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['percent']['carbohydrates']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['per_kg']['carbohydrates']), styleSheet["Normal"])]) data.append([Paragraph(_('Sugar content in carbohydrates'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['carbohydrates_sugar']), styleSheet["Normal"])]) data.append([Paragraph(_('Fat'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['fat']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['percent']['fat']), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['per_kg']['fat']), styleSheet["Normal"])]) data.append([Paragraph(_('Saturated fat content in fats'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['fat_saturated']), styleSheet["Normal"])]) data.append([Paragraph(_('Fibres'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['fibres']), styleSheet["Normal"])]) data.append([Paragraph(_('Sodium'), styleSheet["Normal"]), Paragraph(six.text_type(plan_data['total']['sodium']), styleSheet["Normal"])]) table_style = [] table_style.append(('BOX', (0, 0), (-1, -1), 1.25, colors.black)) table_style.append(('GRID', (0, 0), (-1, -1), 0.40, colors.black)) table_style.append(('SPAN', (0, 0), (-1, 0))) # Title table_style.append(('SPAN', (1, 2), (-1, 2))) # Energy table_style.append(('SPAN', (1, 5), (-1, 5))) # Sugar table_style.append(('SPAN', (1, 7), (-1, 7))) # Saturated fats table_style.append(('SPAN', (1, 8), (-1, 8))) # Fibres table_style.append(('SPAN', (1, 9), (-1, 9))) # Sodium t = Table(data, style=table_style) t._argW[0] = 5 * cm elements.append(t) # Footer, date and info elements.append(Spacer(10 * cm, 0.5 * cm)) created = datetime.date.today().strftime("%d.%m.%Y") url = reverse('nutrition:plan:view', kwargs={'id': plan.id}) p = Paragraph('''<para align="left"> %(date)s - <a href="%(url)s">%(url)s</a> - %(created)s %(version)s </para>''' % {'date': _("Created on the <b>%s</b>") % created, 'created': "wger Workout Manager", 'version': get_version(), 'url': request.build_absolute_uri(url), }, styleSheet["Normal"]) elements.append(p) doc.build(elements) response['Content-Disposition'] = 'attachment; filename=nutritional-plan.pdf' response['Content-Length'] = len(response.content) return response
from setuptools import (setup, find_packages) from wger import get_version with open('README.rst') as readme: long_description = readme.read() with open('requirements.txt') as requirements_production: install_requires = requirements_production.readlines() setup( name='wger', description= 'FLOSS workout, fitness and weight manager/tracker written with Django', long_description=long_description, version=get_version(), url='https://wger.de', author='Roland Geider', author_email='*****@*****.**', license='AGPL3+', packages=find_packages(exclude=['tests']), include_package_data=True, classifiers=[ # http://pypi.python.org/pypi?%3Aaction=list_classifiers 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Other Audience', 'Framework :: Django', 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2',
def processor(request): language = load_language() full_path = request.get_full_path() i18n_path = {} static_path = static('images/logos/logo-marketplace-256.png') for lang in settings.LANGUAGES: i18n_path[lang[0]] = u'/{0}{1}'.format(lang[0], full_path[3:]) context = { # Application version 'version': get_version(), # User language 'language': language, # Available application languages 'languages': settings.LANGUAGES, # The current path 'request_full_path': full_path, # The current full path with host 'request_absolute_path': request.build_absolute_uri(), 'image_absolute_path': request.build_absolute_uri(static_path), # Translation links 'i18n_path': i18n_path, # Flag for guest users 'has_demo_data': request.session.get('has_demo_data', False), # Don't show messages on AJAX requests (they are deleted if shown) 'no_messages': request.META.get('HTTP_X_WGER_NO_MESSAGES', False), # Default cache time for template fragment caching 'cache_timeout': settings.CACHES['default']['TIMEOUT'], # Used for logged in trainers 'trainer_identity': request.session.get('trainer.identity'), } # Pseudo-intelligent navigation here if '/software/' in request.get_full_path() \ or '/contact' in request.get_full_path() \ or '/api/v2' in request.get_full_path(): context['active_tab'] = constants.SOFTWARE_TAB context['show_shariff'] = True elif '/exercise/' in request.get_full_path(): context['active_tab'] = constants.EXERCISE_TAB elif '/nutrition/' in request.get_full_path(): context['active_tab'] = constants.NUTRITION_TAB elif '/weight/' in request.get_full_path(): context['active_tab'] = constants.WEIGHT_TAB elif '/workout/' in request.get_full_path(): context['active_tab'] = constants.WORKOUT_TAB else: context['active_tab'] = constants.USER_TAB return context
def workout_view(request, id, uidb64=None, token=None): ''' Generates a PDF with the contents of the workout, without table for logs ''' # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(content_type='application/pdf') # Load the workout if uidb64 is not None and token is not None: if check_token(uidb64, token): workout = get_object_or_404(Workout, pk=id) else: if request.user.is_anonymous(): return HttpResponseForbidden() workout = get_object_or_404(Workout, pk=id, user=request.user) # Create the PDF object, using the response object as its "file." doc = SimpleDocTemplate(response, pagesize=A4, # pagesize = landscape(A4), leftMargin=cm, rightMargin=cm, topMargin=0.5 * cm, bottomMargin=0.5 * cm, title=_('Workout'), author='wger Workout Manager', subject=_('Workout for %s') % request.user.username) # container for the 'Flowable' objects elements = [] # table data, here we will put the workout info data = [] # Init several counters and markers, this will be used after the iteration to # set different borders and colours day_markers = [] exercise_markers = {} group_exercise_marker = {} group_day_marker = {} # Background colour for days # Reportlab doesn't use the HTML hexadecimal format, but has a range of # 0 till 1, so we have to convert here. header_colour = colors.Color(int('73', 16) / 255.0, int('8a', 16) / 255.0, int('5f', 16) / 255.0) # # Iterate through the Workout # # Days for day in workout.canonical_representation['day_list']: set_count = 1 day_markers.append(len(data)) group_day_marker[day['obj'].id] = {'start': len(data), 'end': len(data)} if not exercise_markers.get(day['obj'].id): exercise_markers[day['obj'].id] = [] p = Paragraph('<para align="center">%(days)s: %(description)s</para>' % {'days': day['days_of_week']['text'], 'description': day['obj'].description}, styleSheet["Bold"]) data.append([p]) # Sets for set in day['set_list']: group_exercise_marker[set['obj'].id] = {'start': len(data), 'end': len(data)} # Exercises for exercise in set['exercise_list']: group_exercise_marker[set['obj'].id]['end'] = len(data) # Note: '+1' here because there's an emtpy cell between days exercise_markers[day['obj'].id].append(len(data) + 1) data.append([set_count, Paragraph(exercise['obj'].name, styleSheet["Small"]), exercise['setting_text']]) set_count += 1 data.append(['']) group_day_marker[day['obj'].id]['end'] = len(data) # Set the widths and heights of rows and columns # Note: 'None' is treated as 'automatic'. Either there is only one value for the whole list # or exactly one for every row/column colwidths = None rowheights = [None] * len(data) table_style = [('FONT', (0, 0), (-1, -1), 'OpenSans'), ('FONTSIZE', (0, 0), (-1, -1), 8), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # Note: a padding of 3 seems to be the default ('LEFTPADDING', (0, 0), (-1, -1), 2), ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('TOPPADDING', (0, 0), (-1, -1), 3), ('BOTTOMPADDING', (0, 0), (-1, -1), 2), ] # Set specific styles, e.g. background for title cells for marker in day_markers: # Set background colour for headings table_style.append(('BACKGROUND', (0, marker), (-1, marker), header_colour)) table_style.append(('BOX', (0, marker), (-1, marker), 1.25, colors.black)) # Make the headings span the whole width table_style.append(('SPAN', (0, marker), (-1, marker))) # Manually set rowheights[marker - 1] = 5 # Combine the cells for exercises on the same set counter = 1 for marker in group_exercise_marker: counter += 1 start_marker = group_exercise_marker[marker]['start'] end_marker = group_exercise_marker[marker]['end'] table_style.append(('SPAN', (0, start_marker), (0, end_marker))) table_style.append(('BOX', (0, start_marker), (-1, end_marker), 0.25, colors.black)) if counter % 2: table_style.append(('BACKGROUND', (0, start_marker), (-1, end_marker), colors.lavender)) for marker in group_day_marker: start_marker = group_day_marker[marker]['start'] end_marker = group_day_marker[marker]['end'] table_style.append(('BOX', (0, start_marker), (-1, end_marker - 2), 1.25, colors.black)) # Set the table data if data: t = Table(data, colwidths, rowheights, style=table_style) # Manually set the width of the columns if len(t._argW) > 1: t._argW[0] = 0.6 * cm # Numbering t._argW[1] = 9 * cm # Exercise t._argW[2] = 4 * cm # Repetitions # There is nothing to output else: t = Paragraph(_('<i>This is an empty workout, what did you expect on the PDF?</i>'), styleSheet["Normal"]) # # Add all elements to the document # # Set the title (if available) if workout.comment: p = Paragraph('<para align="center"><strong>%(description)s</strong></para>' % {'description': workout.comment}, styleSheet["Bold"]) elements.append(p) # Filler elements.append(Spacer(10*cm, 0.5*cm)) # Append the table elements.append(t) # Footer, date and info elements.append(Spacer(10*cm, 0.5*cm)) created = datetime.date.today().strftime("%d.%m.%Y") url = reverse('workout-view', kwargs={'id': workout.id}) p = Paragraph('''<para align="left"> %(date)s - <a href="%(url)s">%(url)s</a> - %(created)s %(version)s </para>''' % {'date': _("Created on the <b>%s</b>") % created, 'created': "wger Workout Manager", 'version': get_version(), 'url': request.build_absolute_uri(url), }, styleSheet["Normal"]) elements.append(p) # write the document and send the response to the browser doc.build(elements) # Create the HttpResponse object with the appropriate PDF headers. response['Content-Disposition'] = 'attachment; filename=Workout-{0}-table.pdf'.format(id) response['Content-Length'] = len(response.content) return response
# wger from wger import get_version with open('README.rst') as readme: long_description = readme.read() with open('requirements.txt') as requirements_production: install_requires = requirements_production.readlines() setup( name='wger', description='FLOSS workout, fitness and weight manager/tracker written with Django', long_description=long_description, version=get_version(), url='https://github.com/wger-project', author='Roland Geider', author_email='*****@*****.**', license='AGPL3+', packages=find_packages(exclude=['tests']), include_package_data=True, classifiers=[ # http://pypi.python.org/pypi?%3Aaction=list_classifiers 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Other Audience', 'Framework :: Django', 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2',
def processor(request): language = load_language() full_path = request.get_full_path() i18n_path = {} for lang in settings.LANGUAGES: i18n_path[lang[0]] = u'/{0}{1}'.format(lang[0], full_path[3:]) context = { # Application version 'version': get_version(), # User language 'language': language, # Available application languages 'languages': settings.LANGUAGES, # The current path 'request_full_path': full_path, # Translation links 'i18n_path': i18n_path, # Translation links 'datepicker_i18n_path': 'js/bootstrap-datepicker/locales/bootstrap-datepicker.{0}.js'.format( language.short_name), # Flag for guest users 'has_demo_data': request.session.get('has_demo_data', False), # Don't show messages on AJAX requests (they are deleted if shown) 'no_messages': request.META.get('HTTP_X_WGER_NO_MESSAGES', False), # Default cache time for template fragment caching 'cache_timeout': settings.CACHES['default']['TIMEOUT'], # Used for logged in trainers 'trainer_identity': request.session.get('trainer.identity'), } # Pseudo-intelligent navigation here if '/software/' in request.get_full_path() \ or '/contact' in request.get_full_path() \ or '/api/v2' in request.get_full_path(): context['active_tab'] = constants.SOFTWARE_TAB context['show_shariff'] = True elif '/exercise/' in request.get_full_path(): context['active_tab'] = constants.EXERCISE_TAB elif '/nutrition/' in request.get_full_path(): context['active_tab'] = constants.NUTRITION_TAB elif '/weight/' in request.get_full_path(): context['active_tab'] = constants.WEIGHT_TAB elif '/workout/' in request.get_full_path(): context['active_tab'] = constants.WORKOUT_TAB else: context['active_tab'] = constants.USER_TAB return context
def get(request): return Response(get_version())
def workout_log(request, id): ''' Generates a PDF with the contents of the given workout See also * http://www.blog.pythonlibrary.org/2010/09/21/reportlab * http://www.reportlab.com/apis/reportlab/dev/platypus.html ''' #Load the workout workout = get_object_or_404(Workout, pk=id, user=request.user) # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment; filename=%s.pdf' % _('Workout') # Create the PDF object, using the response object as its "file." doc = SimpleDocTemplate(response, pagesize=A4, #pagesize = landscape(A4), leftMargin=cm, rightMargin=cm, topMargin=0.5 * cm, bottomMargin=0.5 * cm, title=_('Workout'), author='wger Workout Manager', subject=_('Workout for %s') % request.user.username) # container for the 'Flowable' objects elements = [] # table data, here we will put the workout info data = [] # Init several counters and markers, this will be used after the iteration to # set different borders and colours day_markers = [] exercise_markers = {} group_exercise_marker = {} group_day_marker = {} # Set the number of weeks for this workout # (sets number of columns for the weight/date log) nr_of_weeks = 7 # Set the first column of the weight log, depends on design first_weight_column = 3 # Background colour for days # Reportlab doesn't use the HTML hexadecimal format, but has a range of # 0 till 1, so we have to convert here. header_colour = colors.Color(int('73', 16) / 255.0, int('8a', 16) / 255.0, int('5f', 16) / 255.0) # # Iterate through the Workout # # Days for day in workout.day_set.select_related(): set_count = 1 day_markers.append(len(data)) group_day_marker[day.id] = {'start': len(data), 'end': len(data)} if not exercise_markers.get(day.id): exercise_markers[day.id] = [] days_of_week = [_(day_of_week.day_of_week) for day_of_week in day.day.select_related()] P = Paragraph('<para align="center">%(days)s: %(description)s</para>' % {'days': ', '.join(days_of_week), 'description': day.description}, styleSheet["Bold"]) data.append([P]) # Note: the _('Date') will be on the 3rd cell, but since we make a span # over 3 cells, the value has to be on the 1st one data.append([_('Date') + ' ', '', ''] + [''] * nr_of_weeks) data.append([_('Nr.'), _('Exercise'), _('Reps')] + [_('Weight')] * nr_of_weeks) # Sets for set_obj in day.set_set.select_related(): group_exercise_marker[set_obj.id] = {'start': len(data), 'end': len(data)} # Exercises for exercise in set_obj.exercises.select_related(): group_exercise_marker[set_obj.id]['end'] = len(data) # Note: '+1' here because there's an emtpy cell between days exercise_markers[day.id].append(len(data) + 1) setting_data = [] # Settings for setting in exercise.setting_set.filter(set_id=set_obj.id): if setting.reps == 99: repetitions = '∞' else: repetitions = str(setting.reps) setting_data.append(repetitions) # If there are more than 1 settings, don't output the repetitions # e.g. "4 x 8 8 10 10" is shown only as "8 8 10 10", after all # those 4 sets are not done four times! if len(setting_data) == 0: out = '' # nothing set elif len(setting_data) == 1: out = str(set_obj.sets) + ' × ' + setting_data[0] elif len(setting_data) > 1: out = ', '.join(setting_data) data.append([set_count, Paragraph(exercise.name, styleSheet["Small"]), out] + [''] * nr_of_weeks) set_count += 1 # Note: as above with _('Date'), the _('Impression') has to be here on # the 1st cell so it is shown after adding a span #data.append([_('Impression'), '', '']) set_count += 1 data.append(['']) group_day_marker[day.id]['end'] = len(data) # Set the widths and heights of rows and columns # Note: 'None' is treated as 'automatic'. Either there is only one value for the whole list # or exactly one for every row/column colwidths = None rowheights = [None] * len(data) # Set general table styles #('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), #('BOX', (0,0), (-1,-1), 1.25, colors.black), table_style = [('FONT', (0, 0), (-1, -1), 'OpenSans'), ('FONTSIZE', (0, 0), (-1, -1), 8), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # Note: a padding of 3 seems to be the default ('LEFTPADDING', (0, 0), (-1, -1), 2), ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('TOPPADDING', (0, 0), (-1, -1), 3), ('BOTTOMPADDING', (0, 0), (-1, -1), 2), ] # Set specific styles, e.g. background for title cells for marker in day_markers: # Set background colour for headings table_style.append(('BACKGROUND', (0, marker), (-1, marker), header_colour)) table_style.append(('BOX', (0, marker), (-1, marker), 1.25, colors.black)) table_style.append(('BOX', (0, marker), (-1, marker + 2), 1.25, colors.black)) # Make the headings span the whole width table_style.append(('SPAN', (0, marker), (-1, marker))) # Make the space between days span the whole width table_style.append(('SPAN', (0, marker - 1), (-1, marker - 1))) # Manually set rowheights[marker - 1] = 5 # Make the date span 3 cells and align it to the right table_style.append(('ALIGN', (0, marker + 1), (2, marker + 1), 'RIGHT')) table_style.append(('SPAN', (0, marker + 1), (2, marker + 1))) # Combine the cells for exercises on the same set for marker in group_exercise_marker: start_marker = group_exercise_marker[marker]['start'] end_marker = group_exercise_marker[marker]['end'] table_style.append(('VALIGN', (0, start_marker), (0, end_marker), 'MIDDLE')) table_style.append(('SPAN', (0, start_marker), (0, end_marker))) # Set an alternating background colour for rows for i in exercise_markers: counter = 1 for j in exercise_markers[i]: if not j % 2: table_style.append(('BACKGROUND', (1, j - 1), (-1, j - 1), colors.lavender)) counter += 1 # Make the 'impression' span 3 cells and align it to the right for marker in group_day_marker: start_marker = group_day_marker[marker]['start'] end_marker = group_day_marker[marker]['end'] #table_style.append(('ALIGN', (0, end_marker - 2), (2, end_marker - 2), 'RIGHT')) # There is an empty cell between the day blocks, set a border around them table_style.append(('INNERGRID', (0, start_marker), (- 1, end_marker - 2), 0.25, colors.black)) table_style.append(('BOX', (0, start_marker), (-1, end_marker - 2), 1.25, colors.black)) # Set the table data if data: t = Table(data, colwidths, rowheights, style=table_style) # Manually set the width of the columns for i in range(first_weight_column, nr_of_weeks + first_weight_column): t._argW[i] = 1.8 * cm # Columns for entering the log t._argW[0] = 0.6 * cm # Exercise numbering t._argW[1] = 3.5 * cm # Name of exercise t._argW[2] = 1.9 * cm # Repetitions # There is nothing to output else: t = Paragraph(_('<i>This is an empty workout, what did you expect on the PDF?</i>'), styleSheet["Normal"]) # # Add all elements to the document # # Set the title (if available) if workout.comment: P = Paragraph('<para align="center"><strong>%(description)s</strong></para>' % {'description': workout.comment}, styleSheet["Bold"]) elements.append(P) # Filler P = Paragraph('<para> </para>', styleSheet["Normal"]) elements.append(P) # Append the table elements.append(t) # Footer, add filler paragraph P = Paragraph('<para> </para>', styleSheet["Normal"]) elements.append(P) # Print date and info created = datetime.date.today().strftime("%d.%m.%Y") url = reverse('wger.manager.views.workout.view', kwargs={'id': workout.id}) P = Paragraph('''<para align="left"> %(date)s - <a href="%(url)s">%(url)s</a> - %(created)s %(version)s </para>''' % {'date': _("Created on the <b>%s</b>") % created, 'created': "wger Workout Manager", 'version': get_version(), 'url': request.build_absolute_uri(url), }, styleSheet["Normal"]) elements.append(P) # write the document and send the response to the browser doc.build(elements) return response
def get(request): return Response(get_version(MIN_APP_VERSION, True))
def workout_log(request, id): ''' Generates a PDF with the contents of the given workout See also * http://www.blog.pythonlibrary.org/2010/09/21/reportlab * http://www.reportlab.com/apis/reportlab/dev/platypus.html ''' #Load the workout workout = get_object_or_404(Workout, pk=id, user=request.user) # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment; filename=%s.pdf' % _( 'Workout') # Create the PDF object, using the response object as its "file." doc = SimpleDocTemplate( response, pagesize=A4, #pagesize = landscape(A4), leftMargin=cm, rightMargin=cm, topMargin=0.5 * cm, bottomMargin=0.5 * cm, title=_('Workout'), author='wger Workout Manager', subject=_('Workout for %s') % request.user.username) # container for the 'Flowable' objects elements = [] # table data, here we will put the workout info data = [] # Init several counters and markers, this will be used after the iteration to # set different borders and colours day_markers = [] exercise_markers = {} group_exercise_marker = {} group_day_marker = {} # Set the number of weeks for this workout # (sets number of columns for the weight/date log) nr_of_weeks = 7 # Set the first column of the weight log, depends on design first_weight_column = 3 # Background colour for days # Reportlab doesn't use the HTML hexadecimal format, but has a range of # 0 till 1, so we have to convert here. header_colour = colors.Color( int('73', 16) / 255.0, int('8a', 16) / 255.0, int('5f', 16) / 255.0) # # Iterate through the Workout # # Days for day in workout.day_set.select_related(): set_count = 1 day_markers.append(len(data)) group_day_marker[day.id] = {'start': len(data), 'end': len(data)} if not exercise_markers.get(day.id): exercise_markers[day.id] = [] days_of_week = [ _(day_of_week.day_of_week) for day_of_week in day.day.select_related() ] P = Paragraph( '<para align="center">%(days)s: %(description)s</para>' % { 'days': ', '.join(days_of_week), 'description': day.description }, styleSheet["Bold"]) data.append([P]) # Note: the _('Date') will be on the 3rd cell, but since we make a span # over 3 cells, the value has to be on the 1st one data.append([_('Date') + ' ', '', ''] + [''] * nr_of_weeks) data.append( [_('Nr.'), _('Exercise'), _('Reps')] + [_('Weight')] * nr_of_weeks) # Sets for set_obj in day.set_set.select_related(): group_exercise_marker[set_obj.id] = { 'start': len(data), 'end': len(data) } # Exercises for exercise in set_obj.exercises.select_related(): group_exercise_marker[set_obj.id]['end'] = len(data) # Note: '+1' here because there's an emtpy cell between days exercise_markers[day.id].append(len(data) + 1) setting_data = [] # Settings for setting in exercise.setting_set.filter(set_id=set_obj.id): if setting.reps == 99: repetitions = '∞' else: repetitions = str(setting.reps) setting_data.append(repetitions) # If there are more than 1 settings, don't output the repetitions # e.g. "4 x 8 8 10 10" is shown only as "8 8 10 10", after all # those 4 sets are not done four times! if len(setting_data) == 0: out = '' # nothing set elif len(setting_data) == 1: out = str(set_obj.sets) + ' × ' + setting_data[0] elif len(setting_data) > 1: out = ', '.join(setting_data) data.append([ set_count, Paragraph(exercise.name, styleSheet["Small"]), out ] + [''] * nr_of_weeks) set_count += 1 # Note: as above with _('Date'), the _('Impression') has to be here on # the 1st cell so it is shown after adding a span #data.append([_('Impression'), '', '']) set_count += 1 data.append(['']) group_day_marker[day.id]['end'] = len(data) # Set the widths and heights of rows and columns # Note: 'None' is treated as 'automatic'. Either there is only one value for the whole list # or exactly one for every row/column colwidths = None rowheights = [None] * len(data) # Set general table styles #('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), #('BOX', (0,0), (-1,-1), 1.25, colors.black), table_style = [ ('FONT', (0, 0), (-1, -1), 'OpenSans'), ('FONTSIZE', (0, 0), (-1, -1), 8), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # Note: a padding of 3 seems to be the default ('LEFTPADDING', (0, 0), (-1, -1), 2), ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('TOPPADDING', (0, 0), (-1, -1), 3), ('BOTTOMPADDING', (0, 0), (-1, -1), 2), ] # Set specific styles, e.g. background for title cells for marker in day_markers: # Set background colour for headings table_style.append( ('BACKGROUND', (0, marker), (-1, marker), header_colour)) table_style.append( ('BOX', (0, marker), (-1, marker), 1.25, colors.black)) table_style.append( ('BOX', (0, marker), (-1, marker + 2), 1.25, colors.black)) # Make the headings span the whole width table_style.append(('SPAN', (0, marker), (-1, marker))) # Make the space between days span the whole width table_style.append(('SPAN', (0, marker - 1), (-1, marker - 1))) # Manually set rowheights[marker - 1] = 5 # Make the date span 3 cells and align it to the right table_style.append( ('ALIGN', (0, marker + 1), (2, marker + 1), 'RIGHT')) table_style.append(('SPAN', (0, marker + 1), (2, marker + 1))) # Combine the cells for exercises on the same set for marker in group_exercise_marker: start_marker = group_exercise_marker[marker]['start'] end_marker = group_exercise_marker[marker]['end'] table_style.append( ('VALIGN', (0, start_marker), (0, end_marker), 'MIDDLE')) table_style.append(('SPAN', (0, start_marker), (0, end_marker))) # Set an alternating background colour for rows for i in exercise_markers: counter = 1 for j in exercise_markers[i]: if not j % 2: table_style.append( ('BACKGROUND', (1, j - 1), (-1, j - 1), colors.lavender)) counter += 1 # Make the 'impression' span 3 cells and align it to the right for marker in group_day_marker: start_marker = group_day_marker[marker]['start'] end_marker = group_day_marker[marker]['end'] #table_style.append(('ALIGN', (0, end_marker - 2), (2, end_marker - 2), 'RIGHT')) # There is an empty cell between the day blocks, set a border around them table_style.append(('INNERGRID', (0, start_marker), (-1, end_marker - 2), 0.25, colors.black)) table_style.append(('BOX', (0, start_marker), (-1, end_marker - 2), 1.25, colors.black)) # Set the table data if data: t = Table(data, colwidths, rowheights, style=table_style) # Manually set the width of the columns for i in range(first_weight_column, nr_of_weeks + first_weight_column): t._argW[i] = 1.8 * cm # Columns for entering the log t._argW[0] = 0.6 * cm # Exercise numbering t._argW[1] = 3.5 * cm # Name of exercise t._argW[2] = 1.9 * cm # Repetitions # There is nothing to output else: t = Paragraph( _('<i>This is an empty workout, what did you expect on the PDF?</i>' ), styleSheet["Normal"]) # # Add all elements to the document # # Set the title (if available) if workout.comment: P = Paragraph( '<para align="center"><strong>%(description)s</strong></para>' % {'description': workout.comment}, styleSheet["Bold"]) elements.append(P) # Filler P = Paragraph('<para> </para>', styleSheet["Normal"]) elements.append(P) # Append the table elements.append(t) # Footer, add filler paragraph P = Paragraph('<para> </para>', styleSheet["Normal"]) elements.append(P) # Print date and info created = datetime.date.today().strftime("%d.%m.%Y") url = reverse('wger.manager.views.workout.view', kwargs={'id': workout.id}) P = Paragraph( '''<para align="left"> %(date)s - <a href="%(url)s">%(url)s</a> - %(created)s %(version)s </para>''' % { 'date': _("Created on the <b>%s</b>") % created, 'created': "wger Workout Manager", 'version': get_version(), 'url': request.build_absolute_uri(url), }, styleSheet["Normal"]) elements.append(P) # write the document and send the response to the browser doc.build(elements) return response
def workout_view(request, id, uidb64=None, token=None): ''' Generates a PDF with the contents of the workout, without table for logs ''' # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(content_type='application/pdf') # Load the workout if uidb64 is not None and token is not None: if check_token(uidb64, token): workout = get_object_or_404(Workout, pk=id) else: return HttpResponseForbidden() else: if request.user.is_anonymous(): return HttpResponseForbidden() workout = get_object_or_404(Workout, pk=id, user=request.user) # Create the PDF object, using the response object as its "file." doc = SimpleDocTemplate( response, pagesize=A4, # pagesize = landscape(A4), leftMargin=cm, rightMargin=cm, topMargin=0.5 * cm, bottomMargin=0.5 * cm, title=_('Workout'), author='wger Workout Manager', subject=_('Workout for %s') % request.user.username) # container for the 'Flowable' objects elements = [] # table data, here we will put the workout info data = [] # Init several counters and markers, this will be used after the iteration to # set different borders and colours day_markers = [] exercise_markers = {} group_exercise_marker = {} group_day_marker = {} # Background colour for days # Reportlab doesn't use the HTML hexadecimal format, but has a range of # 0 till 1, so we have to convert here. header_colour = colors.Color( int('73', 16) / 255.0, int('8a', 16) / 255.0, int('5f', 16) / 255.0) # # Iterate through the Workout # # Days for day in workout.canonical_representation['day_list']: set_count = 1 day_markers.append(len(data)) group_day_marker[day['obj'].id] = { 'start': len(data), 'end': len(data) } if not exercise_markers.get(day['obj'].id): exercise_markers[day['obj'].id] = [] p = Paragraph( '<para align="center">%(days)s: %(description)s</para>' % { 'days': day['days_of_week']['text'], 'description': day['obj'].description }, styleSheet["Bold"]) data.append([p]) # Sets for set in day['set_list']: group_exercise_marker[set['obj'].id] = { 'start': len(data), 'end': len(data) } # Exercises for exercise in set['exercise_list']: group_exercise_marker[set['obj'].id]['end'] = len(data) # Note: '+1' here because there's an emtpy cell between days exercise_markers[day['obj'].id].append(len(data) + 1) data.append([ set_count, Paragraph(exercise['obj'].name, styleSheet["Small"]), exercise['setting_text'] ]) set_count += 1 data.append(['']) group_day_marker[day['obj'].id]['end'] = len(data) # Set the widths and heights of rows and columns # Note: 'None' is treated as 'automatic'. Either there is only one value for the whole list # or exactly one for every row/column colwidths = None rowheights = [None] * len(data) table_style = [ ('FONT', (0, 0), (-1, -1), 'OpenSans'), ('FONTSIZE', (0, 0), (-1, -1), 8), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), # Note: a padding of 3 seems to be the default ('LEFTPADDING', (0, 0), (-1, -1), 2), ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('TOPPADDING', (0, 0), (-1, -1), 3), ('BOTTOMPADDING', (0, 0), (-1, -1), 2), ] # Set specific styles, e.g. background for title cells for marker in day_markers: # Set background colour for headings table_style.append( ('BACKGROUND', (0, marker), (-1, marker), header_colour)) table_style.append( ('BOX', (0, marker), (-1, marker), 1.25, colors.black)) # Make the headings span the whole width table_style.append(('SPAN', (0, marker), (-1, marker))) # Manually set rowheights[marker - 1] = 5 # Combine the cells for exercises on the same set counter = 1 for marker in group_exercise_marker: counter += 1 start_marker = group_exercise_marker[marker]['start'] end_marker = group_exercise_marker[marker]['end'] table_style.append(('SPAN', (0, start_marker), (0, end_marker))) table_style.append( ('BOX', (0, start_marker), (-1, end_marker), 0.25, colors.black)) if counter % 2: table_style.append(('BACKGROUND', (0, start_marker), (-1, end_marker), colors.lavender)) for marker in group_day_marker: start_marker = group_day_marker[marker]['start'] end_marker = group_day_marker[marker]['end'] table_style.append(('BOX', (0, start_marker), (-1, end_marker - 2), 1.25, colors.black)) # Set the table data if data: t = Table(data, colwidths, rowheights, style=table_style) # Manually set the width of the columns if len(t._argW) > 1: t._argW[0] = 0.6 * cm # Numbering t._argW[1] = 9 * cm # Exercise t._argW[2] = 4 * cm # Repetitions # There is nothing to output else: t = Paragraph( _('<i>This is an empty workout, what did you expect on the PDF?</i>' ), styleSheet["Normal"]) # # Add all elements to the document # # Set the title (if available) if workout.comment: p = Paragraph( '<para align="center"><strong>%(description)s</strong></para>' % {'description': workout.comment}, styleSheet["Bold"]) elements.append(p) # Filler elements.append(Spacer(10 * cm, 0.5 * cm)) # Append the table elements.append(t) # Footer, date and info elements.append(Spacer(10 * cm, 0.5 * cm)) created = datetime.date.today().strftime("%d.%m.%Y") p = Paragraph( '''<para align="left"> %(date)s - <a href="%(url)s">%(url)s</a> - %(created)s %(version)s </para>''' % { 'date': _("Created on the <b>%s</b>") % created, 'created': "wger Workout Manager", 'version': get_version(), 'url': request.build_absolute_uri(workout.get_absolute_url()), }, styleSheet["Normal"]) elements.append(p) # write the document and send the response to the browser doc.build(elements) # Create the HttpResponse object with the appropriate PDF headers. response[ 'Content-Disposition'] = 'attachment; filename=Workout-{0}-table.pdf'.format( id) response['Content-Length'] = len(response.content) return response
urlpatterns = [ path( 'terms-of-service', TemplateView.as_view(template_name="tos.html"), name='tos', ), path( 'features', views.features, name='features', ), path( 'code', RedirectView.as_view(permanent=True, url='https://github.com/wger-project/wger'), name='code', ), path( 'about-us', TemplateView.as_view(template_name="about_us.html", extra_context={'version': get_version()}), name='about-us', ), path( 'api', TemplateView.as_view(template_name="api.html"), name='api', ), ]
def handle(self, **options): if not settings.MEDIA_ROOT: raise ImproperlyConfigured( 'Please set MEDIA_ROOT in your settings file') remote_url = options['remote_url'] try: val = URLValidator() val(remote_url) except ValidationError: raise CommandError('Please enter a valid URL') exercise_api = "{0}/api/v2/exercise/?limit=999" image_api = "{0}/api/v2/exerciseimage/?exercise={1}" thumbnail_api = "{0}/api/v2/exerciseimage/{1}/thumbnails/" headers = { 'User-agent': default_user_agent('wger/{} + requests'.format(get_version())) } # Get all exercises result = requests.get( exercise_api.format(remote_url), headers=headers).json() for exercise_json in result['results']: exercise_name = exercise_json['name'].encode('utf-8') exercise_uuid = exercise_json['uuid'] exercise_id = exercise_json['id'] self.stdout.write('') self.stdout.write( u"*** Processing {0} (ID: {1}, UUID: {2})".format( exercise_name, exercise_id, exercise_uuid)) try: exercise = Exercise.objects.get(uuid=exercise_uuid) except Exercise.DoesNotExist: self.stdout.write( ' Remote exercise not found in local DB, skipping...') continue # Get all images images = requests.get( image_api.format(remote_url, exercise_id), headers=headers).json() if images['count']: for image_json in images['results']: image_id = image_json['id'] result = requests.get( thumbnail_api.format(remote_url, image_id), headers=headers).json() image_name = os.path.basename(result['original']) self.stdout.write(' Fetching image {0} - {1}'.format( image_id, image_name)) try: image = ExerciseImage.objects.get(pk=image_id) self.stdout.write( ' --> Image already present locally, skipping...' ) continue except ExerciseImage.DoesNotExist: self.stdout.write( ' --> Image not found in local DB, creating now...' ) image = ExerciseImage() image.pk = image_id # Save the downloaded image, see link for details # http://stackoverflow.com/questions/1308386/programmatically-saving-image-to- retrieved_image = requests.get( result['original'], headers=headers) img_temp = NamedTemporaryFile(delete=True) img_temp.write(retrieved_image.content) img_temp.flush() image.exercise = exercise image.is_main = image_json['is_main'] image.status = image_json['status'] image.image.save( os.path.basename(image_name), File(img_temp), ) image.save() else: self.stdout.write( ' No images for this exercise, nothing to do')
def handle(self, **options): if not settings.MEDIA_ROOT: raise ImproperlyConfigured('Please set MEDIA_ROOT in your settings file') remote_url = options['remote_url'] try: val = URLValidator() val(remote_url) except ValidationError: raise CommandError('Please enter a valid URL') exercise_api = "{0}/api/v2/exercise/?limit=999&status=2" image_api = "{0}/api/v2/exerciseimage/?exercise={1}" thumbnail_api = "{0}/api/v2/exerciseimage/{1}/thumbnails/" headers = {'User-agent': default_user_agent('wger/{} + requests'.format(get_version()))} # Get all exercises result = requests.get(exercise_api.format(remote_url), headers=headers).json() for exercise_json in result['results']: exercise_name = exercise_json['name'].encode('utf-8') exercise_uuid = exercise_json['uuid'] exercise_id = exercise_json['id'] self.stdout.write('') self.stdout.write(u"*** Processing {0} (ID: {1}, UUID: {2})".format(exercise_name, exercise_id, exercise_uuid)) try: exercise = Exercise.objects.get(uuid=exercise_uuid) except Exercise.DoesNotExist: self.stdout.write(' Remote exercise not found in local DB, skipping...') continue # Get all images images = requests.get(image_api.format(remote_url, exercise_id), headers=headers).json() if images['count']: for image_json in images['results']: image_id = image_json['id'] result = requests.get(thumbnail_api.format(remote_url, image_id), headers=headers).json() image_name = os.path.basename(result['original']) self.stdout.write(' Fetching image {0} - {1}'.format(image_id, image_name)) try: image = ExerciseImage.objects.get(pk=image_id) self.stdout.write(' --> Image already present locally, skipping...') continue except ExerciseImage.DoesNotExist: self.stdout.write(' --> Image not found in local DB, creating now...') image = ExerciseImage() image.pk = image_id # Save the downloaded image, see link for details # http://stackoverflow.com/questions/1308386/programmatically-saving-image-to- retrieved_image = requests.get(result['original'], headers=headers) img_temp = NamedTemporaryFile(delete=True) img_temp.write(retrieved_image.content) img_temp.flush() image.exercise = exercise image.is_main = image_json['is_main'] image.status = image_json['status'] image.image.save( os.path.basename(image_name), File(img_temp), ) image.save() else: self.stdout.write(' No images for this exercise, nothing to do')