Skip to content

Commit

Permalink
Support of sports assignment on equipments and better equipment bulk …
Browse files Browse the repository at this point in the history
…importing.
  • Loading branch information
Hawk committed Jan 10, 2023
1 parent 450fb9f commit 6589f5e
Show file tree
Hide file tree
Showing 26 changed files with 293 additions and 72 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,17 @@ Here some fixes/improvements i have done in RUNALYZE (see details in the commits
* Support of HTTP proxy via configuration `weather_proxy` for _OpenWeatherMap_ and [MeteostatNet](https://meteostat.net/)
* If loading via _MeteostatNet_ fails, a `Exception` will be thrown
* 2022-12-29: Fix JS error in dataview datepicker; add 20 previous years in dataview datepicker
* 2023-01-08: Add support for sports on equipment level is now relevant while bulk import
* Currently only sports can be assigned on equipment types (=category); now these sports from the type level can be detailed on equipment level
* This affects for a better automatic assigment during bulk imports and is **now required** for bulk import
* This also means if you want no assignment while bulk imports, do not assign a sports on this equipment
* Fixing: now the equipment duration&distance will be decreased, when a activities is being deleted
* Limitations:
* Automatic equipment assignment only works on bulk import (command) and if multiple active equipments for one type are found, this assignment will be ignored
* If you remove in the equipment-type dialog a assigned sports, please remove it also on the equipments (if you has assigned it too); i've skimp the programm support for this ;-)
* Not sure: but it's possible that the Runalyze backup feature is no more working with my changed regarding `equipment_spor`; test it before use it in production
* **Migration 20230101190000 is necessary!** to create the new table `runalyze_equipment_spor`
* **Don't forget to adds sports to your equipments, when you want auto assignment during bulk import**

Please notice:
* All the changes are only done for me to use this great product for me.
Expand Down
54 changes: 54 additions & 0 deletions app/DoctrineMigrations/Version20230101190000.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Runalyze\Migrations;

use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* TSC: Add new table for equipment to sports assigment: equipment_spor (the t is missing because its not the type assignment);
* its the assignment to the equipment and i don't want to change the existing equipment_sport to runalyze_equipment_sport_type)
*/
class Version20230101190000 extends AbstractMigration implements ContainerAwareInterface
{
/** @var ContainerInterface|null */
private $container;

public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}

/**
* @param Schema $schema
*/
public function up(Schema $schema) : void
{
$prefix = $this->container->getParameter('database_prefix');


$this->addSql('CREATE TABLE IF NOT EXISTS `'.$prefix.'equipment_spor` (
`sportid` int(10) unsigned NOT NULL,
`equipment_id` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8');


$this->addSql('ALTER TABLE `'.$prefix.'equipment_spor`
ADD PRIMARY KEY (`sportid`,`equipment_id`), ADD KEY `equipment_id` (`equipment_id`)');

$this->addSql('ALTER TABLE `'.$prefix.'equipment_spor`
ADD CONSTRAINT `runalyze_equipment_spor_ibfk_1` FOREIGN KEY (`sportid`) REFERENCES `runalyze_sport` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `runalyze_equipment_spor_ibfk_2` FOREIGN KEY (`equipment_id`) REFERENCES `runalyze_equipment` (`id`) ON DELETE CASCADE ON UPDATE CASCADE');
}

/**
* @param Schema $schema
*/
public function down(Schema $schema) : void
{
$prefix = $this->container->getParameter('database_prefix');
$this->addSql('DROP TABLE `'.$prefix.'equipment_spor`');
}
}
4 changes: 4 additions & 0 deletions app/Resources/views/my/equipment/form-category.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
{{ form_row(form.sport, {'div_class': 'w100 block with50erLabel'}) }}
<p class="info margin-bottom">
{% trans %}You can limit equipment categories to specific sports.{% endtrans %}
<br/>
Falls du hier eine Sportart entfernst die auch einer konkreten Ausrüstung zugeordnet ist, bitte auch bei der Ausrüstung entfernen.
</p>

{{ form_row(form.maxKm, {'div_class': 'w100 block with50erLabel'}) }}
Expand Down Expand Up @@ -70,6 +72,7 @@
<th>{{ 'Start of use'|trans }}</th>
<th>{{ 'End of use'|trans }}</th>
<th>{{ 'Notes'|trans }}</th>
<th>{{ 'Sport types'|trans }}</th>
<th></th>
</tr>
</thead>
Expand All @@ -83,6 +86,7 @@
<td>{{ entry.dateStart ? entry.dateStart|date('d.m.Y') : '-' }}</td>
<td>{{ entry.dateEnd ? entry.dateEnd|date('d.m.Y') : '-' }}</td>
<td>{{ entry.notes }}</td>
<td>{% for asport in entry.sport %}<li>{{ asport.name }}</li>{% endfor %}</td>
<td><a class="window color-danger" href="{{ url('equipment-delete', { 'id': entry.id, 't': csrf_token('deleteEquipment') }) }}" data-confirm="{% trans %}Do you really want to delete this?{% endtrans %}"><i class="fa fa-trash" title="{{ 'Delete'|trans }}"></i></a></td>
</tr>
{% endfor %}
Expand Down
12 changes: 12 additions & 0 deletions app/Resources/views/my/equipment/form-equipment.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,22 @@
{{ form_row(form.dateEnd, {'div_class': 'w100 block with50erLabel', 'input_unit': '<i class="fa fa-fw fa-calendar"></i>'}) }}
<p class="info margin-bottom">
{% trans %}Objects will be displayed as inactive as soon as they have reached their end date.{% endtrans %}
<br/>
Ausrüstungen, die zum Zeitpunkt einer Bulk-importierten Aktivität nicht gültig sind, werden nich automatisch zugeordnet.
</p>

{{ form_row(form.notes, {'div_class': 'w100 block with50erLabel'}) }}

<br/>

{{ form_row(form.sport, {'div_class': 'w100 block with50erLabel'}) }}
<p class="info margin-bottom">
Wenn im Bulk-Import diese konkrete Ausrüstung automatisch einer Aktivität zugeordnet werden soll,
musst du ein oder mehrere Sportarten (nur die Arten, die der Kategorie zugewiesen wurden, sind verfügbar) zuordnen.
Somit können für eine Ausrüstungskategorie mehrere gleichzeitig Ausrüstungen, aber zu verschiedenen Sportarten, automatisch zugeordnet werden;
oder die Ausrüstung wird beim Import ignoriert.
</p>

{{ form_rest(form) }}

<div class="c margin-top">
Expand Down
24 changes: 24 additions & 0 deletions inc/install/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@ CREATE TABLE IF NOT EXISTS `runalyze_equipment_sport` (
`equipment_typeid` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `runalyze_equipment_spor`
--

CREATE TABLE IF NOT EXISTS `runalyze_equipment_spor` (
`sportid` int(10) unsigned NOT NULL,
`equipment_id` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- --------------------------------------------------------

--
Expand Down Expand Up @@ -559,6 +569,12 @@ ALTER TABLE `runalyze_equipment`
ALTER TABLE `runalyze_equipment_sport`
ADD PRIMARY KEY (`sportid`,`equipment_typeid`), ADD KEY `equipment_typeid` (`equipment_typeid`);

--
-- Indizes für die Tabelle `runalyze_equipment_spor`
--
ALTER TABLE `runalyze_equipment_spor`
ADD PRIMARY KEY (`sportid`,`equipment_id`), ADD KEY `equipment_id` (`equipment_id`);

--
-- Indizes für die Tabelle `runalyze_equipment_type`
--
Expand Down Expand Up @@ -750,6 +766,14 @@ ALTER TABLE `runalyze_equipment_sport`
ADD CONSTRAINT `runalyze_equipment_sport_ibfk_1` FOREIGN KEY (`sportid`) REFERENCES `runalyze_sport` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `runalyze_equipment_sport_ibfk_2` FOREIGN KEY (`equipment_typeid`) REFERENCES `runalyze_equipment_type` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

--
-- Constraints der Tabelle `runalyze_equipment_spor`
--
ALTER TABLE `runalyze_equipment_spor`
ADD CONSTRAINT `runalyze_equipment_spor_ibfk_1` FOREIGN KEY (`sportid`) REFERENCES `runalyze_sport` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `runalyze_equipment_spor_ibfk_2` FOREIGN KEY (`equipment_id`) REFERENCES `runalyze_equipment` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;


--
-- Constraints der Tabelle `runalyze_equipment_type`
--
Expand Down
3 changes: 2 additions & 1 deletion inc/system/class.PDOforRunalyze.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ protected function addAccountIDtoStatement(&$statement) {
&& strpos($statement, PREFIX.'activity_equipment') === false
&& strpos($statement, PREFIX.'activity_tag') === false
&& strpos($statement, PREFIX.'equipment_sport') === false
&& strpos($statement, PREFIX.'equipment_spor') === false
&& strpos($statement, PREFIX.'weathercache') === false
&& strpos($statement, PREFIX.'raceresult') === false
&& strpos($statement, '`accountid`') === false
Expand Down Expand Up @@ -155,7 +156,7 @@ public function insert($table, $columns, $values) {
$table = str_replace(PREFIX, '', $table);

// TODO: TEST IT!
if ($table != 'account' && $table != 'plugin_conf' && $table != 'equipment_sport' && !in_array('accountid', $columns)) {
if ($table != 'account' && $table != 'plugin_conf' && $table != 'equipment_sport' && $table != 'equipment_spor' && !in_array('accountid', $columns)) {
$columns[] = 'accountid';
$values[] = $this->accountID;
}
Expand Down
94 changes: 52 additions & 42 deletions src/CoreBundle/Command/ActivityBulkImportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class ActivityBulkImportCommand extends ContainerAwareCommand
/** @var array */
protected $FailedImports = array();

private $sportEquipment;
// 2 dim-array with 1-Idx=sportId & 2-Idx=Equipment
private $sportEquipment = array();

protected function configure()
{
Expand Down Expand Up @@ -192,27 +193,24 @@ private function addFailedFile($fileName, $error)

/**
* #TSC
* Creates a array where first IDX=sportsId and second IDX=the equipment.
* this is only build with "single-choice" equipments.
* and the main-equipment category/type (see sports configuration); so one IDX_1/sport
* can only have on equipment category with multiple (time-range based) equipments.
* Creates a two-array where first IDX=sportsId and second IDX=the_equipment.
* this is only build for "single-choice" equipments of the account.
* if there are more sports assigned, every sport/equipment combination is stored in the array.
*/
private function createEquiqmentArray(Account $account, OutputInterface $output) {
$accountEquipment = $account->getEquipment();

// build a array where first index is the sports-id
$this->sportEquipment = [];
$output->writeln('Use main equipments for automatic-equipment-mapping of '.$account->getUsername().':');
foreach ($accountEquipment as $acEqp) {
if($acEqp->getType()->getInput() == EquipmentType::CHOICE_SINGLE) {

foreach ($acEqp->getType()->getSport() as $sports) {
if($sports->getMainEquipmenttype() == $acEqp->getType()) {
$idx = $sports->getId();
$this->sportEquipment[$idx][] = $acEqp;

$output->writeln('- '.$acEqp->getName(). ' for sport '.$idx.'/'.$sports->getName());
}
$output->writeln('Use account equipments for automatic-equipment-mapping of '.$account->getUsername().':');
foreach ($accountEquipment as $eqp) {
if($eqp->getType()->getInput() == EquipmentType::CHOICE_SINGLE) {
// use only the sports from the equipment (for the bulk import)
foreach($eqp->getSport() as $sport) {
$idx = $sport->getId();
$this->sportEquipment[$idx][] = $eqp;

$output->writeln('- ' . $eqp->getType()->getName() . ':'.$eqp->getName(). ' -> sport='.$idx.'/'.$sport->getName());
}
}
}
Expand All @@ -221,45 +219,57 @@ private function createEquiqmentArray(Account $account, OutputInterface $output)
/**
* #TSC
* automatic mapping of preloaded/preselected equipments (in method createEquiqmentArray) to the imported activity.
* this means, that the main equipment-type stored in the sportEquipment array are searched for ONE equipment that
* fits to the activity date. if there more than one equipment found, the mapping is not done.
* this means, that the equipments stored in the sportEquipment array are searched for relevant equipment that
* fits to the activity date. if there more than one equipment found within ONE type/category, the mapping is ignored.
*/
private function setEquipmentIfPossible(Training $activity, OutputInterface $output) {
$actDate = $activity->getDateTime();
$sportId = $activity->getSport()->getId();

$foundEqp = null;
$foundEqpCount = 0;
// use equipmentId as key
$equipments = array();

// use typeId(categoryId) as key
$typeUnique = array();

// are there equipments for this (activity) sport?
if (isset($this->sportEquipment[$sportId])) {
// this sport has one equipment-type/category with multiple (time-range based) equipments
foreach ($this->sportEquipment[$sportId] as $acEqp) {

// set the end-date to time 23:59 for the compare
$endDate = null;
if($acEqp->getDateEnd() !== null) {
$endDate = clone $acEqp->getDateEnd();
$endDate->setTime(23,59,59);
}

if(($acEqp->getDateStart() === null || $acEqp->getDateStart() < $actDate)
&& ($endDate === null || $actDate <= $endDate)){
// got to all equipments for this sport
// its possible that several equipments exists with different time-ranges
foreach($this->sportEquipment[$sportId] as $eqp) {

// set the end-date to time 23:59 for the compare
$endDate = null;
if($eqp->getDateEnd() !== null) {
$endDate = clone $eqp->getDateEnd();
$endDate->setTime(23,59,59);
}

$foundEqp = $acEqp;
$foundEqpCount++;
if(($eqp->getDateStart() === null || $eqp->getDateStart() < $actDate) && ($endDate === null || $actDate <= $endDate)) {
// the date fits
$equipments[$eqp->getId()] = $eqp;

//$output->writeln('found: '.$acEqp->getName());
// store if this type already selected (to avoid multiple mappings of the same category)
if(!array_key_exists($eqp->getType()->getId(), $typeUnique)) {
$typeUnique[$eqp->getType()->getId()] = true;
} else {
$typeUnique[$eqp->getType()->getId()] = false;
}
}
}
}

if($foundEqpCount == 1 && $foundEqp != null) {
//$output->writeln('set: '.$foundEqp->getName());
$activity->addEquipment($foundEqp);
} elseif ($foundEqpCount > 1) {
$output->writeln('<fg=yellow>more than one main equipment found for sport '.$sportId.'/'.$activity->getSport()->getName().
' and date '.$actDate->format('Y-m-d H:i').'; mapping canceled!</>');
if(!empty($equipments)) {
foreach($equipments as $k => $v) {
// add every equipment, if it not "unique"
if($typeUnique[$v->getType()->getId()]) {
$activity->addEquipment($v);
} else {
$output->writeln('<fg=yellow>more than one active equipment found for activity-date=' . $actDate->format('Y-m-d H:i') .
': category=' . $v->getType()->getName() . ' and equipment=' . $v->getId() . '/' . $v->getName() .
'; mapping canceled for this category!</>');
}
}
}
}
}
}
4 changes: 4 additions & 0 deletions src/CoreBundle/Component/Tool/Backup/AbstractBackup.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ final public function run()
$this->Prefix.'equipment_type',
$this->Prefix.'equipment_sport',
$this->Prefix.'equipment',
$this->Prefix.'equipment_spor',
$this->Prefix.'activity_equipment',
$this->Prefix.'tag',
$this->Prefix.'activity_tag',
Expand Down Expand Up @@ -135,6 +136,9 @@ private function addConditionToQuery(&$query, $tableName)
} elseif ($tableName == $this->Prefix.'equipment_sport') {
$ids = $this->fetchEquipmentTypeIDs();
$query .= ' WHERE `equipment_typeid` IN('.implode(',', $ids).')';
} elseif ($tableName == $this->Prefix.'equipment_spor') {
$ids = $this->fetchEquipmentIDs();
$query .= ' WHERE `equipment_id` IN('.implode(',', $ids).')';
} elseif ($tableName == $this->Prefix.'activity_equipment') {
$ids = $this->fetchEquipmentIDs();
$query .= ' WHERE `equipmentid` IN('.implode(',', $ids).')';
Expand Down
1 change: 1 addition & 0 deletions src/CoreBundle/Component/Tool/Backup/BulkInserter.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected function prepareStatement($tableName, array $columns, $accountID, $dat
if (
$accountID !== false &&
$tableName != $databasePrefix.'equipment_sport' &&
$tableName != $databasePrefix.'equipment_spor' &&
$tableName != $databasePrefix.'activity_equipment' &&
$tableName != $databasePrefix.'activity_tag'
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ protected function expectedTables()
'runalyze_user',
'runalyze_equipment_type',
'runalyze_equipment_sport',
'runalyze_equipment_spor',
'runalyze_equipment',
'runalyze_activity_equipment',
'runalyze_activity_tag',
Expand Down
Loading

0 comments on commit 6589f5e

Please sign in to comment.