diff --git a/sql/01_data_exploration.sql b/sql/01_data_exploration.sql new file mode 100644 index 0000000..f8f4d04 --- /dev/null +++ b/sql/01_data_exploration.sql @@ -0,0 +1,847 @@ +/* + * TB2 Data exploration + * + * We will set out to the understand the database structure, + * and to understand how this data actually produces climbs on a Tension Board. + * + * + * This data was downloaded via boardlib (https://github.com/lemeryfertitta/BoardLib) on 2026-03-14. + * It is clear from the `kits` table that it was updated on 2026-01-22 (well, most of it). + */ + +-------------------------------------------------------------------------------- + +/* + * Understanding the board + * + * Goal: + * 1. Shallow dive into the database structure + * 2. Identify tha main tables + * 3. Understand how the tension board data works, and how frames are mapped to board + */ + +SELECT 'android_metadata' AS table_name, COUNT(*) AS rows FROM android_metadata +UNION ALL SELECT 'ascents', COUNT(*) FROM ascents +UNION ALL SELECT 'attempts', COUNT(*) FROM attempts +UNION ALL SELECT 'beta_links', COUNT(*) FROM beta_links +UNION ALL SELECT 'bids', COUNT(*) FROM bids +UNION ALL SELECT 'circuits', COUNT(*) FROM circuits +UNION ALL SELECT 'circuits_climbs', COUNT(*) FROM circuits_climbs +UNION ALL SELECT 'climb_cache_fields', COUNT(*) FROM climb_cache_fields +UNION ALL SELECT 'climb_random_positions', COUNT(*) FROM climb_random_positions +UNION ALL SELECT 'climb_stats', COUNT(*) FROM climb_stats +UNION ALL SELECT 'climbs', COUNT(*) FROM climbs +UNION ALL SELECT 'difficulty_grades', COUNT(*) FROM difficulty_grades +UNION ALL SELECT 'holes', COUNT(*) FROM holes +UNION ALL SELECT 'kits', COUNT(*) FROM kits +UNION ALL SELECT 'layouts', COUNT(*) FROM layouts +UNION ALL SELECT 'leds', COUNT(*) FROM leds +UNION ALL SELECT 'placement_roles', COUNT(*) FROM placement_roles +UNION ALL SELECT 'placements', COUNT(*) FROM placements +UNION ALL SELECT 'product_sizes', COUNT(*) FROM product_sizes +UNION ALL SELECT 'product_sizes_layouts_sets', COUNT(*) FROM product_sizes_layouts_sets +UNION ALL SELECT 'products', COUNT(*) FROM products +UNION ALL SELECT 'products_angles', COUNT(*) FROM products_angles +UNION ALL SELECT 'sets', COUNT(*) FROM sets +UNION ALL SELECT 'shared_syncs', COUNT(*) FROM shared_syncs +UNION ALL SELECT 'tags', COUNT(*) FROM tags +UNION ALL SELECT 'user_permissions', COUNT(*) FROM user_permissions +UNION ALL SELECT 'user_syncs', COUNT(*) FROM user_syncs +UNION ALL SELECT 'users', COUNT(*) FROM users +UNION ALL SELECT 'walls', COUNT(*) FROM walls +UNION ALL SELECT 'walls_sets', COUNT(*) FROM walls_sets +ORDER BY rows DESC; +/* +table_name |rows | +--------------------------+------+ +climb_stats |147046| +climbs |128762| +climb_cache_fields | 90670| +beta_links | 9500| +leds | 3388| +placements | 1299| +holes | 967| +difficulty_grades | 39| +attempts | 38| +product_sizes_layouts_sets| 36| +kits | 25| +products_angles | 25| +shared_syncs | 15| +product_sizes | 9| +placement_roles | 8| +sets | 6| +layouts | 3| +products | 2| +android_metadata | 1| +ascents | 0| +bids | 0| +circuits | 0| +circuits_climbs | 0| +climb_random_positions | 0| +tags | 0| +user_permissions | 0| +user_syncs | 0| +users | 0| +walls | 0| +walls_sets | 0| + */ + + +/* + * It is clear that climb_stats (147k), climbs (128k), and climb_cache_fields (90k) are the most important. + * beta_links just includes instagram links showing beta. + * + * Some Obesrvations: + * - climb_stats has more entries than climbs -- potentially multiple entries per climb? Maybe same climb at different angles? + * - placements/holes are likely reference tables for the physical board --hole positions, holds, etc. + * - placement roles might be start/finish/middle/foot hold, etc. Or hold type? + * - plenty of empty tables. Some should correspond to a specific user if you download the DB using broadlib with the -u flag. I couldn't get it working though. + * + * Let's start by looking at some sample data. + */ + +-------------------------------------------------------- + + +/* + * CLIMBS + */ + +SELECT * FROM climbs; +/* +uuid |layout_id|setter_id|setter_username|name |description |hsm|edge_left|edge_right|edge_bottom|edge_top|angle|frames_count|frames_pace|frames |is_draft|is_listed|created_at |is_nomatch| +--------------------------------+---------+---------+---------------+--------------+------------+---+---------+----------+-----------+--------+-----+------------+-----------+-----------------------------------------+--------+---------+--------------------------+----------+ +00163801596af1064d549ad75b684539| 9| 33802|vinsewah |Duroxmanie 2.0|No matching | 3| 8| 88| 32| 128| | 1| 0|p3r4p29r2p59r1p65r2p75r3p89r2p157r4p158r4| 0| 1|2021-02-16 09:13:28.000000| 1| +001945feb7509ce231c9d8b237082a39| 9| 30521|ssssss |1 am | | 3| 24| 72| 56| 128| | 1| 0|p18r1p22r1p57r2p70r2p149r3p161r4p162r4 | 0| 1|2021-02-13 01:53:03.000000| 0| +002078ce5b07166d80e87d2cafc94dab| 9| 61740|rockindude |test69 |No matching.| 7| 24| 64| 64| 152| | 1| 0|p16r2p49r1p70r2p83r3p127r2p140r2p191r1 | 0| 1|2021-12-22 03:41:04.000000| 1| +0027cc6eea485099809f5336a0452564| 9| 56399|memphisben |Pre Game |No matching.| 1| 8| 40| 40| 128| | 1| 0|p22r1p49r1p74r3p76r4p78r2p80r2 | 0| 1|2021-02-13 01:52:54.000000| 1| +002e2db25b124ff5719afdb2c6732b2c| 9| 33924|jfisch040 |Yoooooooooo | | 9| 16| 48| 4| 152| | 1| 0|p1r3p14r2p67r1p73r2p80r2p279r4 | 0| 1|2021-02-13 01:52:57.000000| 0| + + * The frams column is what actually determines the holds on the climb, and what role they are. + * There are some climb characteristics (name, desceription, whether or not matching is allowed, setter info, edges, whether it is listed). + * The UUID is how we link the specifc climb to the other tables. + * What is hsm? + */ + +SELECT * FROM climb_cache_fields; +/* +climb_uuid |ascensionist_count|display_difficulty|quality_average| +--------------------------------+------------------+------------------+---------------+ +0004edf6aeac9618d96a3b949cd9a724| 2| 24.0| 3.0| +00072fbd8c22711ef3532a5017c1a5c2| 4| 19.25| 3.0| +00178ae931e482c3e6337d86d761936b| 1| 27.0| 3.0| +0020974d7ee7f1b6d78b44a70f3fa27b| 1| 24.0| 3.0| +0024b68ebc5cbbcfbe653ec4ed224271| 1| 23.0| 3.0| + * + * climb_uuid, ascentionist_count, display difficulty, and quality_average. + */ + +SELECT * FROM climb_stats; +/* +climb_uuid |angle|display_difficulty|benchmark_difficulty|ascensionist_count|difficulty_average|quality_average|fa_username |fa_at | +--------------------------------+-----+------------------+--------------------+------------------+------------------+---------------+--------------------+-------------------+ +0004edf6aeac9618d96a3b949cd9a724| 40| 24.0| | 2| 24.0| 3.0|david.p.kunz |2020-03-23 23:52:37| +00072fbd8c22711ef3532a5017c1a5c2| 25| 19.25| | 4| 19.25| 3.0|free.and.independent|2019-10-05 01:55:14| +0008d8af4649234054bea434aaeabaab| 45| 20.0| | 2| 20.0| 2.0|judemandudeman |2018-01-30 03:18:13| +000eb831d3a1e92ea8fdec2518fd77d3| 20| 18.0| | 1| 18.0| 3.0|dasruch17 |2019-03-15 15:46:06| +000eb831d3a1e92ea8fdec2518fd77d3| 40| 23.0| | 1| 23.0| 3.0|hunter.tension |2021-06-27 22:41:10| + * + * So per UUID, we have a lot of the same info as climb_cache_fields. + * We also have angle and first ascent information + * + * + * + * Why are there more climb_stats than climbs? + * Let's see if it is due to multiple angles per climb. + */ + +SELECT + cs.climb_uuid, + COUNT(*) AS angle_count +FROM climb_stats cs +GROUP BY cs.climb_uuid +ORDER BY angle_count DESC; +/* +climb_uuid |angle_count| +--------------------------------+-----------+ +0227943857CA4D55849E8D351775B10A| 14| +18E0834CBBB64952AE12BB7DD7F56E28| 14| +197A52F20F424C3DB935993E2385758D| 14| +2048D3DB80DD443BA4BB37F263984929| 14| +2A740F4239AD4D498F65780626D7CECA| 14| + * + * + * Yep, some UUIDs correspond to multiple angles. + * + * How many climbs are there if we don't take angle into account? + * + */ + +SELECT COUNT(DISTINCT climb_uuid) FROM climb_stats; +/* + * +COUNT(DISTINCT climb_uuid)| +--------------------------+ + 90494| + * So 90k climbs in total. + * + * Let's take a look at difficulty_grades. + */ + +SELECT * FROM difficulty_grades; +/* +difficulty|boulder_name|route_name|is_listed| +----------+------------+----------+---------+ + 1|1a/V0 |2b/5.1 | 0| + 2|1b/V0 |2c/5.2 | 0| + 3|1c/V0 |3a/5.3 | 0| + 4|2a/V0 |3b/5.3 | 0| + 5|2b/V0 |3c/5.4 | 0| + 6|2c/V0 |4a/5.5 | 0| + 7|3a/V0 |4b/5.6 | 0| + 8|3b/V0 |4c/5.7 | 0| + 9|3c/V0 |5a/5.8 | 0| + 10|4a/V0 |5b/5.9 | 1| + 11|4b/V0 |5c/5.10a | 1| + 12|4c/V0 |6a/5.10b | 1| + 13|5a/V1 |6a+/5.10c | 1| + 14|5b/V1 |6b/5.10d | 1| + 15|5c/V2 |6b+/5.11a | 1| + 16|6a/V3 |6c/5.11b | 1| + 17|6a+/V3 |6c+/5.11c | 1| + 18|6b/V4 |7a/5.11d | 1| + 19|6b+/V4 |7a+/5.12a | 1| + 20|6c/V5 |7b/5.12b | 1| + 21|6c+/V5 |7b+/5.12c | 1| + 22|7a/V6 |7c/5.12d | 1| + 23|7a+/V7 |7c+/5.13a | 1| + 24|7b/V8 |8a/5.13b | 1| + 25|7b+/V8 |8a+/5.13c | 1| + 26|7c/V9 |8b/5.13d | 1| + 27|7c+/V10 |8b+/5.14a | 1| + 28|8a/V11 |8c/5.14b | 1| + 29|8a+/V12 |8c+/5.14c | 1| + 30|8b/V13 |9a/5.14d | 1| + 31|8b+/V14 |9a+/5.15a | 1| + 32|8c/V15 |9b/5.15b | 1| + 33|8c+/V16 |9b+/5.15c | 1| + 34|9a/V17 |9c/5.15d | 0| + 35|9a+/V18 |9c+/5.16a | 0| + 36|9b/V19 |10a/5.16b | 0| + 37|9b+/V20 |10a+/5.16c| 0| + 38|9c/V21 |10b/5.16d | 0| + 39|9c+/V22 |10b+/5.17a| 0| + * + * So this just tells us what the numeric value corresponds to in terms of a boulder grade or a route grade. + */ + +-------------------------------------------------------- + +/* + * BOARDS, PLACEMENTS and HOLDS + */ + + +SELECT * FROM placements; +/* +id|layout_id|hole_id|set_id|default_placement_role_id| +--+---------+-------+------+-------------------------+ + 1| 9| 2| 8| 3| + 2| 9| 10| 8| 3| + 3| 9| 317| 8| 1| + 4| 9| 325| 8| 1| + 5| 9| 320| 8| 1| + * + * So we have specific layouts, hole_id, set_id, and default_palcement_role_id. + * Let's examine each of these, starting with layout. + */ + +SELECT * FROM layouts; +/* +id|product_id|name |instagram_caption|is_mirrored|is_listed|password|created_at | +--+----------+----------------------+-----------------+-----------+---------+--------+--------------------------+ +10| 5|Tension Board 2 Mirror| | 1| 1| |2022-08-19 14:52:19.570731| +11| 5|Tension Board 2 Spray | | 0| 1| |2022-10-26 03:42:45.736011| + 9| 4|Original Layout | | 1| 1| |2017-01-01 00:45:51.000000| + * + * So this tells us which specific board, along with whether or not it is mirrored. + * So both TB1 and TB2 Mirror are, while TB2 Spray is not. + * + * There is a distinction between product_id. I imagine it is just TB1 vs TB2. + * + */ + +SELECT * FROM products; +/* +id|name |is_listed|password|min_count_in_frame|max_count_in_frame| +--+---------------+---------+--------+------------------+------------------+ + 4|Tension Board | 1| | 2| 35| + 5|Tension Board 2| 1| | 2| 35| + * + * Yep, product ID just tells us which board we're working with. + * + * May as well see product_sizes, produce_sizes_layouts_sets, and products_angles while we're at it. + * Let's start with the latter, since it's self-explanatory. + */ + +SELECT * FROM products_angles; +/* +product_id|angle| +----------+-----+ + 4| 20| + 4| 25| + 4| 30| + 4| 35| + 4| 40| + 4| 45| + 4| 50| + 5| 20| + 5| 25| + 5| 30| + 5| 35| + 5| 40| + 5| 45| + 5| 50| + 5| 55| + 4| 0| + 4| 5| + 4| 10| + 4| 15| + 5| 0| + 5| 5| + 5| 10| + 5| 15| + 5| 60| + 5| 65| + * + * Yep, just tells us the angles that the TB1 and TB2 can go. + * Let's look at the sizes. + */ + +SELECT * FROM product_sizes; +/* +id|product_id|edge_left|edge_right|edge_bottom|edge_top|name |description |image_filename |position|is_listed| +--+----------+---------+----------+-----------+--------+-----------------+---------------------------------+----------------------+--------+---------+ + 1| 4| 0| 96| 0| 156|Full Wall |Rows: KB1, KB2, 1-18¶Columns: A-K|product_sizes/1.png | 0| 1| + 2| 4| 0| 96| 4| 156|Half Kickboard |Rows: KB2, 1-18¶Columns: A-K |product_sizes/2.png | 1| 1| + 3| 4| 0| 96| 8| 156|No Kickboard |Rows: 1-18¶Columns: A-K |product_sizes/3-v2.png| 2| 1| + 4| 4| 0| 96| 8| 132|Short |Rows: 1-15¶Columns: A-K |product_sizes/4-v2.png| 3| 1| + 5| 4| 16| 80| 8| 132|Short & Narrow |Rows: 1-15¶Columns: B.5-I.5 |product_sizes/5-v3.png| 4| 1| + 6| 5| -68| 68| 0| 144|12 high x 12 wide| |product_sizes/6.png | 1| 1| + 7| 5| -68| 68| 0| 120|10 high x 12 wide| |product_sizes/7.png | 2| 1| + 8| 5| -44| 44| 0| 144|12 high x 8 wide | |product_sizes/8.png | 3| 1| + 9| 5| -44| 44| 0| 120|10 high x 8 wide | |product_sizes/9.png | 4| 1| + * + * This just gives us product_size_id, and some info. + * We'll be mainly interested in the TB2 Mirror 12x12, so we want product_size_id=6. + */ + +SELECT * FROM product_sizes_layouts_sets; +/* +id|product_size_id|layout_id|set_id|image_filename |is_listed| +--+---------------+---------+------+------------------------------------------------+---------+ + 1| 1| 9| 8|product_sizes_layouts_sets/1.png | 1| + 2| 1| 9| 9|product_sizes_layouts_sets/2.png | 1| + 3| 1| 9| 10|product_sizes_layouts_sets/3.png | 1| + 4| 1| 9| 11|product_sizes_layouts_sets/4.png | 1| + 5| 2| 9| 8|product_sizes_layouts_sets/5.png | 1| + 6| 2| 9| 9|product_sizes_layouts_sets/6.png | 1| + 7| 2| 9| 10|product_sizes_layouts_sets/7.png | 1| + 8| 2| 9| 11|product_sizes_layouts_sets/8.png | 1| + 9| 3| 9| 8|product_sizes_layouts_sets/9.png | 1| +10| 3| 9| 9|product_sizes_layouts_sets/10.png | 1| +11| 3| 9| 10|product_sizes_layouts_sets/11.png | 1| +12| 3| 9| 11|product_sizes_layouts_sets/12.png | 1| +13| 4| 9| 8|product_sizes_layouts_sets/13.png | 1| +14| 4| 9| 9|product_sizes_layouts_sets/14.png | 1| +15| 4| 9| 10|product_sizes_layouts_sets/15.png | 1| +16| 4| 9| 11|product_sizes_layouts_sets/16.png | 1| +17| 5| 9| 8|product_sizes_layouts_sets/17.png | 1| +18| 5| 9| 9|product_sizes_layouts_sets/18.png | 1| +19| 5| 9| 10|product_sizes_layouts_sets/19.png | 1| +20| 5| 9| 11|product_sizes_layouts_sets/20.png | 1| +23| 7| 10| 12|product_sizes_layouts_sets/23.png | 1| +25| 8| 10| 12|product_sizes_layouts_sets/25.png | 1| +26| 8| 10| 13|product_sizes_layouts_sets/26.png | 1| +27| 9| 10| 12|product_sizes_layouts_sets/27.png | 1| +28| 9| 10| 13|product_sizes_layouts_sets/28.png | 1| +29| 6| 11| 12|product_sizes_layouts_sets/12x12-tb2-wood.png | 1| +30| 6| 11| 13|product_sizes_layouts_sets/12x12-tb2-plastic.png| 1| +31| 7| 11| 12|product_sizes_layouts_sets/12x10-tb2-wood.png | 1| +32| 7| 11| 13|product_sizes_layouts_sets/12x10-tb2-plastic.png| 1| +33| 8| 11| 12|product_sizes_layouts_sets/8x12-tb2-wood.png | 1| +34| 8| 11| 13|product_sizes_layouts_sets/8x12-tb2-plastic.png | 1| +35| 9| 11| 12|product_sizes_layouts_sets/8x10-tb2-wood.png | 1| +36| 9| 11| 13|product_sizes_layouts_sets/8x10-tb2-plastic.png | 1| +21| 6| 10| 12|product_sizes_layouts_sets/21-2.png | 1| +22| 6| 10| 13|product_sizes_layouts_sets/22-2.png | 1| +24| 7| 10| 13|product_sizes_layouts_sets/24-2.png | 1| + * + * These tell the product_size_id, which might be useful later. + * We'll mostly be interested in the TB2 12x12 Mirror, so thats product_id=6, layout_id=10. + * This tells us which images we'll want to look at for later. We'll make some heat maps, so 21-2.png and 22-2.png are out pictures. + * We'll combine them into one in GIMP and call it tb2_board_12x12_composite.png + * + * Back to understanding the placements. We'll look at holes next. + */ + + +SELECT * FROM holes; +/* +id|product_id|name|x |y |mirrored_hole_id|mirror_group| +--+----------+----+--+---+----------------+------------+ + 1| 4|A,18| 8|152| 11| 0| + 2| 4|B,18|16|152| 10| 0| + 3| 4|C,18|24|152| 9| 0| + 4| 4|D,18|32|152| 8| 0| + 5| 4|E,18|40|152| 7| 0| + * + * The coordinates must be the position on the board. + * These make sense with the product sizes above -- it is clear what the boundaries are (from the edge features). + * + * With the TB1 and TB2 Mirror, you can mirror climbs. So the mirror_hole_id must be where the associated mirror hole is. + * Not sure about the mirror_group. + * + * Let's look at sets next. + */ + +SELECT * FROM sets; +/* +id|name |hsm| +--+--------+---+ + 8|Set A | 1| + 9|Set B | 2| +10|Set C | 4| +11|Foot Set| 8| +12|Wood | 1| +13|Plastic | 2| + * + * So these tell us the corresponding set of the board. + * Any board will often use a combination: for example, the TB2 Mirror uses both wood and plastic. + * No idea what hsm means. Probably something to do with "hold set ____" + * + * Next let's understand this default_placement_id. We'll look at placement_roles. + */ + +SELECT * FROM placement_roles; +/* +id|product_id|position|name |full_name|led_color|screen_color| +--+----------+--------+------+---------+---------+------------+ + 1| 4| 1|start |Start |00FF00 |00DD00 | + 3| 4| 3|finish|Finish |FF0000 |FF0000 | + 4| 4| 4|foot |Foot Only|FF00FF |FF00FF | + 5| 5| 1|start |Start |00FF00 |00DD00 | + 7| 5| 3|finish|Finish |FF0000 |FF0000 | + 8| 5| 4|foot |Foot Only|FF00FF |FF00FF | + 2| 4| 2|middle|Middle |0000FF |0066FF | + 6| 5| 2|middle|Middle |0000FF |0066FF | + * + * These are indeed start/finish/middle/foot, but with 4 being for the TB1 and 4 being for the TB2. + * So r5 = start, r6 = middle, r7 = finish, r8 = foot only on TB2. Similarly with r1-r4 on TB1. + * + * Also tells us which colours are used by the board and the app. + * + * Lastly, let's look at the LEDs table since it is one of the bigger ones, and the LEDs relate to the placements/holes. + */ + +SELECT * FROM leds; +/* +id|product_size_id|hole_id|position| +--+---------------+-------+--------+ + 1| 1| 379| 0| + 2| 1| 389| 1| + 3| 1| 378| 2| + 4| 1| 388| 3| + 5| 1| 377| 4| + * + * product_size_id tells us which size of board this led belongs to, and the hole_id tells us the corresponding hole. + */ + +-------------------------- + +/* + * STRAGGLERS + * + * Let's take a look at some of the non-empty tables that are left over. + * + */ + +SELECT * FROM beta_links; + +/* + * yep, just instagram links of people doing the climb..' + * + * what about attemps? + */ + +SELECT * FROM attempts; +/* +id|position|name | +--+--------+---------+ + 1| 1|Flash | + 2| 2|2 tries | + 3| 3|3 tries | + 4| 4|4 tries | + 5| 5|5 tries | + 6| 6|6 tries | + 7| 7|7 tries | + 8| 8|8 tries | + 9| 9|9 tries | +10| 10|10 tries | +11| 100|1 day | +12| 200|2 days | +13| 300|3 days | +14| 400|4 days | +15| 500|5 days | +16| 600|6 days | +17| 700|7 days | +18| 800|8 days | +19| 900|9 days | +20| 1000|10 days | +21| 10000|1 month | +22| 20000|2 months | +23| 30000|3 months | +24| 40000|4 months | +25| 50000|5 months | +26| 60000|6 months | +27| 70000|7 months | +28| 80000|8 months | +29| 90000|9 months | +30| 100000|10 months| +31| 110000|11 months| +32| 120000|12 months| +33| 1000000|1 year | +34| 2000000|2 years | +35| 3000000|3 years | +36| 4000000|4 years | +37| 5000000|5 years | + 0| 0|Unknown | + * + * Not really sure what to make of this table. + * + * Let's do kits next. + */ + +SELECT * FROM kits; +/* +serial_number|name |is_autoconnect|is_listed|created_at |updated_at | +-------------+--------------------------+--------------+---------+--------------------------+--------------------------+ +84668 |TB2 Spray | 0| 1|2022-10-06 18:37:44.021720|2022-10-06 18:37:44.021720| +84669 |TB2 Mirror | 0| 1|2022-10-06 18:37:33.495216|2022-10-06 18:37:33.495216| +84670 |TB1 | 0| 1|2022-10-06 18:37:22.548024|2022-10-06 18:37:22.548024| +84685 |Tension Board | 1| 1|2023-09-26 01:41:54.577139|2023-09-26 01:41:54.577139| +81114 |Tension Board 20 degrees | 0| 1|2023-12-01 20:48:43.013655|2023-12-13 01:17:00.160708| +84771 |Tension Board 2 40 degrees| 0| 1|2023-12-01 20:48:22.110324|2023-12-13 01:16:36.884910| +84863 |Tension Board 1 | 0| 1|2024-04-05 18:15:05.684248|2024-04-05 18:15:05.684248| +84783 |Tension Board 2 | 0| 1|2024-04-05 18:15:20.260241|2024-04-05 18:15:20.260241| +81106 |Tension Board | 0| 1|2024-07-24 18:18:43.531949|2024-07-24 18:18:43.531949| +84818 |Tension Board 2 | 0| 1|2024-07-24 18:19:00.103068|2024-07-24 18:19:00.103068| +84562 |Tension Board 1 | 0| 1|2025-01-22 01:20:51.718259|2025-01-22 01:20:51.718259| +84911 |Tension Board 2 | 0| 1|2025-01-22 01:21:06.579570|2025-01-22 01:21:06.579570| +84776 |Tension Board 2 | 0| 1|2025-02-26 20:15:08.355773|2025-02-26 20:15:08.355773| +84439 |Tension Board 20 degrees | 0| 1|2023-12-01 20:49:00.365305|2025-04-17 03:00:37.331399| +81199 |Tension Board 1 | 0| 1|2025-05-12 23:08:24.829370|2025-05-12 23:08:24.829370| +841062 |Tension Board 2 | 0| 1|2025-05-12 23:08:41.275933|2025-05-12 23:08:41.275933| +84938 |Tension Board Mirror | 0| 1|2025-09-18 23:12:12.029864|2025-09-18 23:12:12.029864| +84937 |Tension Board Spray | 0| 1|2025-09-18 23:12:24.734650|2025-09-18 23:12:24.734650| +91396 |Tension Board Full | 0| 1|2025-09-22 03:51:35.982550|2025-09-22 03:51:35.982550| +841139 |Tension Board 2 | 0| 1|2025-09-22 03:51:52.379943|2025-09-22 03:51:52.379943| +841066 |Tension Board Mirror | 0| 1|2025-11-30 19:58:24.143556|2025-11-30 19:58:24.143556| +84983 |Tension Board Spray | 0| 1|2025-11-30 19:58:40.803397|2025-11-30 19:58:40.803397| +84870 |Tension Board 2 | 0| 1|2025-12-12 17:52:43.951207|2025-12-12 17:52:43.951207| +841240 |Tension Board Left | 0| 1|2026-01-22 20:08:37.220459|2026-01-22 20:08:37.220459| +91744 |Tension Board Right | 0| 1|2026-01-22 20:08:48.978201|2026-01-22 20:08:48.978201| +* +* Not sure what to make of this table either. I guess just products they have? Some are fixed, some are not, and some are parts of the full board? +* +* Shared syncs next. +*/ + +SELECT * FROM shared_syncs; +/* +table_name |last_synchronized_at | +--------------------------+--------------------------+ +attempts |2024-06-22 23:43:48.952599| +products |2024-06-22 23:43:48.952599| +product_sizes |2024-06-22 23:43:48.952599| +holes |2024-06-22 23:43:48.952599| +leds |2024-06-22 23:43:48.952599| +sets |2024-06-22 23:43:48.952599| +products_angles |2024-06-22 23:43:48.952599| +placements |2024-06-22 23:43:48.952599| +product_sizes_layouts_sets|2024-06-25 18:11:42.775946| +layouts |2025-08-22 00:38:52.971578| +placement_roles |2025-08-23 05:22:16.042123| +climbs |2026-01-31 01:07:23.833934| +climb_stats |2026-01-31 01:22:10.306067| +beta_links |2026-01-09 02:29:20.891517| +kits |2026-01-22 20:08:48.978201| + * + * Possibly when each table in this DB was synced? + */ + +SELECT fa_at FROM climb_stats ORDER BY fa_at DESC; + +/* +fa_at | +-------------------+ +2026-01-31 01:20:12| +2026-01-31 01:11:42| +2026-01-31 01:07:46| + * + * Yep, last logged first ascent agrees with this. + * + * This leaves android_meta. + */ + +SELECT * FROM android_metadata; +/* +locale| +------+ +en_US | + * + * Nothing of value here. + */ + +------------------------------------------------------- + +/* + * Trying to understand some more about the data. + * + * Let's start with udnerstanding more about layouts and placements. + */ + +-- How many climbs per layout? What about stats? +SELECT + c.layout_id, + COUNT(DISTINCT c.uuid) AS climbs, + COUNT(cs.climb_uuid) AS stats_rows +FROM climbs c +LEFT JOIN climb_stats cs ON c.uuid = cs.climb_uuid +GROUP BY c.layout_id; + +/* +layout_id|climbs|stats_rows| +---------+------+----------+ + 9| 68511| 76256| + 10| 39396| 44986| + 11| 20855| 25804| + * + * So 68k for the TB1, 39k for TB2 Mirror, and 20k for TB2 spray. + */ + +-- How many placements per layout? +SELECT + layout_id, + COUNT(*) AS placement_count +FROM placements +GROUP BY layout_id; +/* +layout_id|placement_count| +---------+---------------+ + 9| 303| + 10| 498| + 11| 498| + * + * This makes sense. I know the TB2 Mirror 12x12 is supposed to have 498 holds. + * + * How do the sets relate to the layout? + */ + +SELECT + l.id AS layout_id, + l.name AS layout_name, + p.set_id, + s.name AS set_name, + COUNT(p.id) AS placement_count +FROM layouts l +JOIN placements p ON l.id = p.layout_id +JOIN sets s ON p.set_id = s.id +GROUP BY l.id, l.name, p.set_id, s.name +ORDER BY l.id, p.set_id; +/* +layout_id|layout_name |set_id|set_name|placement_count| +---------+----------------------+------+--------+---------------+ + 9|Original Layout | 8|Set A | 82| + 9|Original Layout | 9|Set B | 83| + 9|Original Layout | 10|Set C | 84| + 9|Original Layout | 11|Foot Set| 54| + 10|Tension Board 2 Mirror| 12|Wood | 242| + 10|Tension Board 2 Mirror| 13|Plastic | 256| + 11|Tension Board 2 Spray | 12|Wood | 242| + 11|Tension Board 2 Spray | 13|Plastic | 256| + * + * So this tells us which sets belong to which board. + * With the TB2 Mirror, all the wood (242) and plastic (256) add up to 498, as expected. + */ + +-- Where do the placements go? +SELECT + p.id AS placement_id, + p.layout_id, + l.name AS layout_name, + p.hole_id, + h.name AS hole_name, + h.x, + h.y, + s.name AS set_name +FROM placements p +JOIN holes h ON p.hole_id = h.id +JOIN sets s ON p.set_id = s.id +JOIN layouts l ON p.layout_id = l.id +ORDER BY p.layout_id, p.id; + +/* + placement_id|layout_id|layout_name |hole_id|hole_name|x |y |set_name| +------------+---------+---------------+-------+---------+--+---+--------+ + 1| 9|Original Layout| 2|B,18 |16|152|Set A | + 2| 9|Original Layout| 10|J,18 |80|152|Set A | + 3| 9|Original Layout| 317|B,3 |16| 32|Set A | + 4| 9|Original Layout| 325|J,3 |80| 32|Set A | + 5| 9|Original Layout| 320|E,3 |40| 32|Set A | + * Okay, so we can figure out from this table a) which board (sepcific layout) b) which hole c) the specific (x,y)-cordinate that the placement goes. + * Looking at the product_sizes table, we have our coordinate system (x,y). For the TB2, x ranges from -64 to 64 and y from 0 to 144. + * Note that there are a few inches on the left and right of the board, so this makes sense. + */ + +-- How many LEDs total, and by product_size? +SELECT + ps.id AS product_size_id, + ps.name AS size_name, + p.name AS product_name, + COUNT(l.id) AS led_count +FROM leds l +JOIN product_sizes ps ON l.product_size_id = ps.id +JOIN products p ON ps.product_id = p.id +GROUP BY ps.id, ps.name, p.name +ORDER BY ps.id; +/* + * There seems to be a discrepency as there are 578 LEDs on the 12x12 TB2, but only 498 holds. + * +product_size_id|size_name |product_name |led_count| +---------------+-----------------+---------------+---------+ + 1|Full Wall |Tension Board | 389| + 2|Half Kickboard |Tension Board | 379| + 3|No Kickboard |Tension Board | 368| + 4|Short |Tension Board | 305| + 5|Short & Narrow |Tension Board | 217| + 6|12 high x 12 wide|Tension Board 2| 578| + 7|10 high x 12 wide|Tension Board 2| 479| + 8|12 high x 8 wide |Tension Board 2| 368| + 9|10 high x 8 wide |Tension Board 2| 305| + + * + * Looking at the installation guide (https://artrock.at/wp-content/uploads/2026/03/MIRROR_2024_TensionBoard2_InstallGuide_1_22_26.pdf), + * the main grid (17 x 18 = 306) + the subgrid (16 x 17 = 272) adds up to 578. + * + * It seems as though we're not using all the holes. + * + * + * Let's dive deeper into the data to unravel this. + * + */ + +-- Check if every hole has an LED +SELECT + COUNT(DISTINCT h.id) AS total_holes, + COUNT(DISTINCT l.hole_id) AS holes_with_leds, + COUNT(DISTINCT h.id) - COUNT(DISTINCT l.hole_id) AS holes_without_leds +FROM holes h +LEFT JOIN leds l ON h.id = l.hole_id; +/* + * + total_holes|holes_with_leds|holes_without_leds| +-----------+---------------+------------------+ + 967| 967| 0| + */ + + + +-- For TB2 (product_id=5), which holes are used by layouts vs LEDs? +-- First, what holes does layout 10 use? +SELECT COUNT(DISTINCT p.hole_id) AS holes_in_layout_10 +FROM placements p +WHERE p.layout_id = 10; +/* +holes_in_layout_10| +------------------+ + 498| + */ + +-- What's the hole range for each product? +SELECT + h.product_id, + MIN(h.id) AS min_hole_id, + MAX(h.id) AS max_hole_id, + COUNT(*) AS total_holes +FROM holes h +GROUP BY h.product_id; + +/* +product_id|min_hole_id|max_hole_id|total_holes| +----------+-----------+-----------+-----------+ + 4| 1| 389| 389| + 5| 390| 967| 578| + */ + + +-- Do LED positions overlap or are they sequential per size? +SELECT + product_size_id, + MIN(position) AS min_pos, + MAX(position) AS max_pos, + COUNT(*) AS led_count +FROM leds +GROUP BY product_size_id +ORDER BY product_size_id; + +/* +product_size_id|min_pos|max_pos|led_count| +---------------+-------+-------+---------+ + 1| 0| 389| 389| + 2| 0| 389| 379| + 3| 22| 389| 368| + 4| 0| 304| 305| + 5| 0| 216| 217| + 6| 0| 577| 578| + 7| 0| 478| 479| + 8| 0| 367| 368| + 9| 0| 304| 305| + */ + +-- Which holes in the TB2 range (390-967) are NOT used by layout 10? +SELECT * +FROM holes h +WHERE h.product_id = 5 +AND h.id NOT IN ( + SELECT DISTINCT p.hole_id + FROM placements p + WHERE p.layout_id = 10 +); + +/* +id |product_id|name |x |y |mirrored_hole_id|mirror_group| +---+----------+------+---+--+----------------+------------+ +391| 5|-64,12|-64|12| 951| 0| +424| 5|-60,8 |-60| 8| 949| 0| +423| 5|-60,16|-60|16| 948| 0| +422| 5|-60,24|-60|24| 947| 0| +416| 5|-60,72|-60|72| 941| 0| + */ + + +--------------------------------------------------------------- +/* + * So we understand HOW the board works pretty well now. Let's summarize. + * - There are about 128k climbs, across 3 layouts -- the TB1, TB2 (Mirror) and TB2 (Spray). + * - There are about 147k statistcs for climbs. This includes multiple angles for each climb. + * - Some key features are the frames, the angle, and the layout_id (the latter determins the board, the former the actual climb on the board) + * - Hold positions are decoded via mapping placements to (x,y) coordinates (from the holes tables) + * - There are four hold types: start, middle, finish, foot. 498 holds on the TB2. + * - There are different hold sets (e.g., Wood/Plastic on TB2). + * - LEDs just map on to holes, and light up depending on our frames. There are 80 unused LEDs on the TB2. + */ + + + +