def get_project(self, shortname): """ Try and retrieve a project for this user, with the given shortname :param shortname: :return: """ return Project.get(self._id, shortname)
def create_project(self, shortname, title): """ Create a new project :param shortname: :param title: :return: """ return Project.create(self._id, shortname, title)
def get_projects(self, filter_by=None, filter=None): """ Get all of the user's projects @param filter_by: @param filter: @return: """ return Project.all(self._id, filter_by, filter)
async def complete(self, context=None, bot=None): """ Finish the sprint, calculate all the WPM and XP and display results :return: """ # Print the 'Results coming up shortly' message await self.say(lib.get_string('sprint:resultscomingsoon', self._guild), context, bot) # Create array to use for storing the results results = [] # If the sprint has already completed, stop. if self._completed != 0: return # Mark this sprint as complete so the cron doesn't pick it up and start processing it again self.set_complete() # Get all the users taking part users = self.get_users() # Loop through them and get their full sprint info for user_id in users: user = User(user_id, self._guild, context=context, bot=bot, channel=self.get_channel()) user_sprint = self.get_user_sprint(user_id) # If it's a non-word count sprint, we don't need to do anything with word counts. if user_sprint['sprint_type'] == Sprint.SPRINT_TYPE_NO_WORDCOUNT: # Just give them the completed sprint stat and XP. await user.add_xp(Experience.XP_COMPLETE_SPRINT) user.add_stat('sprints_completed', 1) # Push user to results results.append({ 'user': user, 'wordcount': 0, 'xp': Experience.XP_COMPLETE_SPRINT, 'type': user_sprint['sprint_type'] }) else: # If they didn't submit an ending word count, use their current one if user_sprint['ending_wc'] == 0: user_sprint['ending_wc'] = user_sprint['current_wc'] # Now we only process their result if they have declared something and it's different to their starting word count user_sprint['starting_wc'] = int(user_sprint['starting_wc']) user_sprint['current_wc'] = int(user_sprint['current_wc']) user_sprint['ending_wc'] = int(user_sprint['ending_wc']) user_sprint['timejoined'] = int(user_sprint['timejoined']) if user_sprint['ending_wc'] > 0 and user_sprint[ 'ending_wc'] != user_sprint['starting_wc']: wordcount = user_sprint['ending_wc'] - user_sprint[ 'starting_wc'] time_sprinted = self._end_reference - user_sprint[ 'timejoined'] # If for some reason the timejoined or sprint.end_reference are 0, then use the defined sprint length instead if user_sprint[ 'timejoined'] <= 0 or self._end_reference == 0: time_sprinted = self._length # Calculate the WPM from their time sprinted wpm = Sprint.calculate_wpm(wordcount, time_sprinted) # See if it's a new record for the user user_record = user.get_record('wpm') wpm_record = True if user_record is None or wpm > int( user_record) else False # If it is a record, update their record in the database if wpm_record: user.update_record('wpm', wpm) # Give them XP for finishing the sprint await user.add_xp(Experience.XP_COMPLETE_SPRINT) # Increment their stats user.add_stat('sprints_completed', 1) user.add_stat('sprints_words_written', wordcount) user.add_stat('total_words_written', wordcount) # Increment their words towards their goal await user.add_to_goals(wordcount) # If they were writing in a Project, update its word count. if user_sprint['project'] is not None: project = Project(user_sprint['project']) project.add_words(wordcount) # is there an event running on this server? event = Event.get_by_guild(self._guild) if event and event.is_running(): event.add_words(user.get_id(), wordcount) # Push user to results results.append({ 'user': user, 'wordcount': wordcount, 'wpm': wpm, 'wpm_record': wpm_record, 'xp': Experience.XP_COMPLETE_SPRINT, 'type': user_sprint['sprint_type'] }) # Sort the results results = sorted(results, key=itemgetter('wordcount'), reverse=True) # Now loop through them again and apply extra XP, depending on their position in the results position = 1 highest_word_count = 0 for result in results: if result['wordcount'] > highest_word_count: highest_word_count = result['wordcount'] # If the user finished in the top 5 and they weren't the only one sprinting, earn extra XP is_sprint_winner = result['wordcount'] == highest_word_count if position <= 5 and len(results) > 1: extra_xp = math.ceil( Experience.XP_WIN_SPRINT / (self.WINNING_POSITION if is_sprint_winner else position)) result['xp'] += extra_xp await result['user'].add_xp(extra_xp) # If they actually won the sprint, increase their stat by 1 # Since the results are in order, the highest word count will be set first # which means that any subsequent users with the same word count have tied for 1st place if position == 1 or result['wordcount'] == highest_word_count: result['user'].add_stat('sprints_won', 1) position += 1 # Post the final message with the results if len(results) > 0: position = 1 message = lib.get_string('sprint:results:header', self._guild) for result in results: if result['type'] == Sprint.SPRINT_TYPE_NO_WORDCOUNT: message = message + lib.get_string( 'sprint:results:row:nowc', self._guild).format( result['user'].get_mention(), result['xp']) else: message = message + lib.get_string( 'sprint:results:row', self._guild).format( position, result['user'].get_mention(), result['wordcount'], result['wpm'], result['xp']) # If it's a new PB, append that string as well if result['wpm_record'] is True: message = message + lib.get_string( 'sprint:results:pb', self._guild) message = message + '\n' position += 1 else: message = lib.get_string('sprint:nowordcounts', self._guild) # Send the message, either via the context or directly to the channel await self.say(message, context, bot)
def main(): args = parse_args() # override log_path if set before actually creating logger if args.log_path: global_configuration.log_path = args.log_path # ------------------------------------------------------- # now import other packages import utils.config as cfgutil from utils.logging import logger from utils.config import Config as cfg from utils.strings import generate_random_key as rands, pad_lines from proc.project import ProcessProject from structures.project import Project import colorama colorama.init() __root__ = global_configuration.root __cfg__ = global_configuration.cfg __src__ = global_configuration.src # change stream_handler level to info logger.set_level('INFO', logger.LOGGER_STREAMHANDLER) logger.increase_verbosity(args.verbosity) logger.debug('app args: %s', str(args), skip_format=True) # convert list ["key:value", "key2:value2", ...] to dict {key:value, key2:value2} # default value for this dictionary is string value 'master' args.git_branch = dict(tuple(d.split(':', 1)) for d in args.git_branch) args.git_branch = defaultdict(lambda: 'master', **args.git_branch) # default value for this dictionary is string value '' args.git_commit = dict(tuple(d.split(':', 1)) for d in args.git_commit) args.git_commit = defaultdict(lambda: '', **args.git_commit) # all the paths depend on the project name project_name = args.project # all configuration files are cfg/<project-name> directory if not set otherwise project_dir = args.config_dir if args.config_dir else os.path.join( __cfg__, project_name) if args.config_dir: if not os.path.exists(args.config_dir): logger.warning('invalid config location given: %s', project_dir) project_dir = os.path.join(__cfg__, project_name) logger.warning('will try to use %s instead', project_dir) if not os.path.exists(project_dir): logger.error('no valid configuration found for the project %s', project_name) sys.exit(1) # execute on demand using pbs/local or other system if args.execute: project_execute_path = os.path.join(project_dir, 'execute.sh') if os.path.exists(project_execute_path): with open(project_execute_path, 'r') as fp: project_execute_script = fp.read() else: project_execute_script = '\n'.join([ '#!/bin/bash --login', 'echo "<ci-hpc-install>"', '<ci-hpc-install>', 'echo "<ci-hpc-exec>"', '<ci-hpc-exec>', 'exit $?', ]) install_args = [os.path.join(__root__, 'share', 'install.sh')] name = 'tmp.entrypoint-%d-%s.sh' % (time.time(), rands(6)) bash_path = os.path.join(__root__, 'tmp', name) logger.debug('Generating script %s', bash_path) with open(bash_path, 'w') as fp: exec_args = [ sys.executable, os.path.join(__src__, 'main.py'), ' '.join(args.step), ] for arg in ['project', 'git_url', 'config_dir']: value = getattr(args, arg) if value: exec_args.append('--%s=%s' % (arg.replace('_', '-'), str(value))) for arg in ['git_branch', 'git_commit']: value = getattr(args, arg) for k, v in value.items(): exec_args.append('--%s=%s:%s' % (arg.replace('_', '-'), k, v)) fp.write( cfgutil.configure_string( project_execute_script, { 'ci-hpc-exec': ' '.join(exec_args), 'ci-hpc-install': ' '.join(install_args), 'ci-hpc-exec-no-interpret': ' '.join(exec_args[1:]), })) os.chmod(bash_path, 0o777) logger.debug('ci-hpc-exec set to %s', ' '.join(exec_args), skip_format=True) # execute on demand using specific system (local/pbs for now) logger.info('executing script %s using %s system', bash_path, args.execute) if args.execute == 'local': p = subprocess.Popen([bash_path]) # def cleanup(): # logger.info('CLEANING PROCESSES') # os.killpg(0, signal.SIGKILL) # atexit.register(cleanup) p.wait() elif args.execute == 'pbs': logger.debug('running cmd: %s', str(['qsub', bash_path]), skip_format=True) qsub_output = str( subprocess.check_output(['qsub', bash_path]).decode()).strip() cmd = [ sys.executable, os.path.join(__src__, 'wait_for.py'), qsub_output, '--timeout=%d' % args.timeout, '--check-interval=%d' % args.check_interval, '--live-log=%s' % global_configuration.log_path, '--quiet', ] logger.info('waiting for job (%s) to finish', qsub_output) subprocess.Popen(cmd).wait() os.unlink(bash_path) sys.exit(0) if global_configuration.tty: logger.info( 'Started ci-hpc in a %s mode', colorama.Back.BLUE + colorama.Fore.CYAN + colorama.Style.BRIGHT + ' tty ' + colorama.Style.RESET_ALL) else: logger.info('Started ci-hpc *not* in a tty mode') # this file contains only variables which can be used in config.yaml variables_path = os.path.join(project_dir, 'variables.yaml') # this file contains all the sections and steps for installation and testing config_path = os.path.join(project_dir, 'config.yaml') # update cpu count variables = cfgutil.load_config(variables_path) # variables['cpu-count'] = args.cpu_count # load config project_config = cfgutil.configure_file(config_path, variables) logger.debug('yaml configuration: \n%s', pad_lines(utils.strings.to_yaml(project_config)), skip_format=True) # specify some useful global arguments which will be available in the config file global_args_extra = { 'project-name': project_name, 'project-dir': project_dir, 'arg': dict( branch=defaultdict(lambda: 'master', **dict(args.git_branch)), commit=defaultdict(lambda: '', **dict(args.git_commit)), ) } # parse config project_definition = Project(project_name, **project_config) project_definition.update_global_args(global_args_extra) project = ProcessProject(project_definition) logger.info('processing project %s, section %s', project_name, args.step) # ----------------------------------------------------------------- if 'install' in args.step: project.process_section(project_definition.install) if 'test' in args.step: project.process_section(project_definition.test)
def get_projects(self): """ Get all of the user's projects :return: """ return Project.all(self._id)
async def run_join(self, context, arg1=None, arg2=None): """ Join the sprint, with an optional starting word count and project shortname :param opt1: Argument 1 of the join command :param opt2: Argument 2 of the join command :return: """ user = User(context.message.author.id, context.guild.id, context) sprint = Sprint(user.get_guild()) project_id = None starting_wc = None sprint_type = None # If there is no active sprint, then just display an error if not sprint.exists(): return await context.send( user.get_mention() + ', ' + lib.get_string('sprint:err:noexists', user.get_guild())) # Are we using the `same` keyword? if arg1 == "same": # Okay, check for their most recent sprint record most_recent = user.get_most_recent_sprint(sprint) if most_recent is not None: starting_wc = most_recent['ending_wc'] project_id = most_recent['project'] sprint_type = most_recent['sprint_type'] # Can't use the second argument in this case. arg2 = None # Are we doing a no wordcount sprint? E.g. we are sprinting or using the functionality for something else. elif arg1 in ["edit", "non-wc"]: sprint_type = Sprint.SPRINT_TYPE_NO_WORDCOUNT # Can't use the second argument in this case. arg2 = None else: # Convert starting_wc to int if we can starting_wc = lib.is_number(arg1) # If the starting_wc is still False at this point, just set it to 0 if not starting_wc: starting_wc = 0 # If the user is already sprinting, then just update their starting wordcount if sprint.is_user_sprinting(user.get_id()): # Update the sprint_users record. We set their current_wc to the same as starting_wc here, otherwise if they join with, say 50 and their current remains 0 # then if they run a status, or in the ending calculations, it will say they wrote -50. sprint.update_user(user.get_id(), start=starting_wc, current=starting_wc, sprint_type=sprint_type) # If it's a non-wordcount sprint, send a different message. if sprint_type == Sprint.SPRINT_TYPE_NO_WORDCOUNT: await context.send(user.get_mention() + ', ' + lib.get_string( 'sprint:join:update:no_wordcount', user.get_guild())) else: # Send message back to channel letting them know their starting word count was updated await context.send(user.get_mention() + ', ' + lib.get_string( 'sprint:join:update', user.get_guild()).format(starting_wc) ) else: # Join the sprint sprint.join(user.get_id(), starting_wc=starting_wc, sprint_type=sprint_type) if sprint_type == Sprint.SPRINT_TYPE_NO_WORDCOUNT: await context.send(user.get_mention() + ', ' + lib.get_string( 'sprint:join:update:no_wordcount', user.get_guild())) else: # Send message back to channel letting them know their starting word count was updated await context.send(user.get_mention() + ', ' + lib.get_string( 'sprint:join', user.get_guild()).format(starting_wc)) # Currently this is the only usage of argument 2. shortname = arg2 # If a project shortname is supplied, try to set that as what the user is sprinting for. if shortname is not None or project_id is not None: # Did we supply the project by name? if shortname is not None: # Convert to lowercase for searching. shortname = shortname.lower() # Make sure the project exists. project = user.get_project(shortname) # Or do we already have the ID from using 'same' and getting from previous sprint? elif project_id is not None: project = Project(project_id) # If that did not yield a valid project, send an error message. if not project: return await context.send( user.get_mention() + ', ' + lib.get_string('project:err:noexists', user.get_guild()).format(shortname)) sprint.set_project(project.get_id(), user.get_id()) return await context.send( user.get_mention() + ', ' + lib.get_string('sprint:project', user.get_guild()).format( project.get_title()))